summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/api/handlers/compat/containers.go23
-rw-r--r--pkg/api/handlers/compat/containers_create.go237
-rw-r--r--pkg/api/handlers/compat/containers_logs.go6
-rw-r--r--pkg/api/handlers/compat/events.go15
-rw-r--r--pkg/api/handlers/compat/images.go4
-rw-r--r--pkg/api/handlers/compat/images_search.go20
-rw-r--r--pkg/api/handlers/compat/networks.go82
-rw-r--r--pkg/api/handlers/compat/swagger.go14
-rw-r--r--pkg/api/handlers/libpod/containers.go30
-rw-r--r--pkg/api/handlers/libpod/images.go11
-rw-r--r--pkg/api/handlers/libpod/networks.go14
-rw-r--r--pkg/api/handlers/types.go11
-rw-r--r--pkg/api/server/register_networks.go57
-rw-r--r--pkg/api/server/server.go24
-rw-r--r--pkg/bindings/connection.go16
-rw-r--r--pkg/bindings/containers/attach.go52
-rw-r--r--pkg/bindings/containers/containers.go6
-rw-r--r--pkg/bindings/test/containers_test.go12
-rw-r--r--pkg/domain/entities/containers.go6
-rw-r--r--pkg/domain/entities/engine_container.go6
-rw-r--r--pkg/domain/entities/images.go2
-rw-r--r--pkg/domain/entities/network.go5
-rw-r--r--pkg/domain/entities/types.go2
-rw-r--r--pkg/domain/entities/volumes.go4
-rw-r--r--pkg/domain/infra/abi/containers.go23
-rw-r--r--pkg/domain/infra/abi/containers_runlabel.go3
-rw-r--r--pkg/domain/infra/abi/cp.go2
-rw-r--r--pkg/domain/infra/abi/images.go4
-rw-r--r--pkg/domain/infra/abi/images_list.go16
-rw-r--r--pkg/domain/infra/abi/network.go15
-rw-r--r--pkg/domain/infra/abi/play.go2
-rw-r--r--pkg/domain/infra/abi/terminal/sigproxy_linux.go5
-rw-r--r--pkg/domain/infra/abi/volumes.go19
-rw-r--r--pkg/domain/infra/tunnel/containers.go15
-rw-r--r--pkg/domain/infra/tunnel/network.go20
-rw-r--r--pkg/domain/infra/tunnel/volumes.go22
-rw-r--r--pkg/spec/security.go2
-rw-r--r--pkg/spec/storage.go6
-rw-r--r--pkg/specgen/generate/container_create.go17
-rw-r--r--pkg/specgen/generate/namespaces.go2
-rw-r--r--pkg/specgen/generate/oci.go2
-rw-r--r--pkg/specgen/generate/ports.go46
-rw-r--r--pkg/terminal/util.go6
-rw-r--r--pkg/util/utils.go19
-rw-r--r--pkg/varlinkapi/images.go2
45 files changed, 545 insertions, 362 deletions
diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go
index cae8f88fd..00be8e845 100644
--- a/pkg/api/handlers/compat/containers.go
+++ b/pkg/api/handlers/compat/containers.go
@@ -410,25 +410,14 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON,
return nil, err
}
- networkSettingsDefault := types.DefaultNetworkSettings{
- EndpointID: "",
- Gateway: "",
- GlobalIPv6Address: "",
- GlobalIPv6PrefixLen: 0,
- IPAddress: "",
- IPPrefixLen: 0,
- IPv6Gateway: "",
- MacAddress: l.Config().StaticMAC.String(),
- }
-
- networkSettingsBase := types.NetworkSettingsBase{
- Ports: ports,
+ n, err := json.Marshal(inspect.NetworkSettings)
+ if err != nil {
+ return nil, err
}
- networkSettings := types.NetworkSettings{
- NetworkSettingsBase: networkSettingsBase,
- DefaultNetworkSettings: networkSettingsDefault,
- Networks: nil,
+ networkSettings := types.NetworkSettings{}
+ if err := json.Unmarshal(n, &networkSettings); err != nil {
+ return nil, err
}
c := types.ContainerJSON{
diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go
index 4ce31cc83..87c95a24c 100644
--- a/pkg/api/handlers/compat/containers_create.go
+++ b/pkg/api/handlers/compat/containers_create.go
@@ -1,27 +1,19 @@
package compat
import (
- "context"
"encoding/json"
- "fmt"
"net/http"
- "strings"
- "github.com/containers/common/pkg/config"
+ "github.com/containers/podman/v2/cmd/podman/common"
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
- image2 "github.com/containers/podman/v2/libpod/image"
"github.com/containers/podman/v2/pkg/api/handlers"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
- "github.com/containers/podman/v2/pkg/namespaces"
- "github.com/containers/podman/v2/pkg/rootless"
- "github.com/containers/podman/v2/pkg/signal"
- createconfig "github.com/containers/podman/v2/pkg/spec"
+ "github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/containers/podman/v2/pkg/domain/infra/abi"
"github.com/containers/podman/v2/pkg/specgen"
- "github.com/containers/storage"
"github.com/gorilla/schema"
"github.com/pkg/errors"
- "golang.org/x/sys/unix"
)
func CreateContainer(w http.ResponseWriter, r *http.Request) {
@@ -56,220 +48,27 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "NewFromLocal()"))
return
}
- containerConfig, err := runtime.GetConfig()
+
+ // Take input structure and convert to cliopts
+ cliOpts, args, err := common.ContainerCreateToContainerCLIOpts(input)
if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "GetConfig()"))
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "make cli opts()"))
return
}
- cc, err := makeCreateConfig(r.Context(), containerConfig, input, newImage)
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "makeCreatConfig()"))
+ sg := specgen.NewSpecGenerator(newImage.ID(), cliOpts.RootFS)
+ if err := common.FillOutSpecGen(sg, cliOpts, args); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "fill out specgen"))
return
}
- cc.Name = query.Name
- utils.CreateContainer(r.Context(), w, runtime, &cc)
-}
-
-func makeCreateConfig(ctx context.Context, containerConfig *config.Config, input handlers.CreateContainerConfig, newImage *image2.Image) (createconfig.CreateConfig, error) {
- var (
- err error
- init bool
- )
- env := make(map[string]string)
- stopSignal := unix.SIGTERM
- if len(input.StopSignal) > 0 {
- stopSignal, err = signal.ParseSignal(input.StopSignal)
- if err != nil {
- return createconfig.CreateConfig{}, err
- }
- }
-
- workDir, err := newImage.WorkingDir(ctx)
+ ic := abi.ContainerEngine{Libpod: runtime}
+ report, err := ic.ContainerCreate(r.Context(), sg)
if err != nil {
- return createconfig.CreateConfig{}, err
- }
- if workDir == "" {
- workDir = "/"
- }
- if len(input.WorkingDir) > 0 {
- workDir = input.WorkingDir
- }
-
- // Only use image's Cmd when the user does not set the entrypoint
- if input.Entrypoint == nil && len(input.Cmd) == 0 {
- cmdSlice, err := newImage.Cmd(ctx)
- if err != nil {
- return createconfig.CreateConfig{}, err
- }
- input.Cmd = cmdSlice
- }
-
- if input.Entrypoint == nil {
- entrypointSlice, err := newImage.Entrypoint(ctx)
- if err != nil {
- return createconfig.CreateConfig{}, err
- }
- input.Entrypoint = entrypointSlice
- }
-
- stopTimeout := containerConfig.Engine.StopTimeout
- if input.StopTimeout != nil {
- stopTimeout = uint(*input.StopTimeout)
- }
- c := createconfig.CgroupConfig{
- Cgroups: "", // podman
- Cgroupns: "", // podman
- CgroupParent: "", // podman
- CgroupMode: "", // podman
- }
- security := createconfig.SecurityConfig{
- CapAdd: input.HostConfig.CapAdd,
- CapDrop: input.HostConfig.CapDrop,
- LabelOpts: nil, // podman
- NoNewPrivs: false, // podman
- ApparmorProfile: "", // podman
- SeccompProfilePath: "",
- SecurityOpts: input.HostConfig.SecurityOpt,
- Privileged: input.HostConfig.Privileged,
- ReadOnlyRootfs: input.HostConfig.ReadonlyRootfs,
- ReadOnlyTmpfs: false, // podman-only
- Sysctl: input.HostConfig.Sysctls,
- }
-
- var netmode namespaces.NetworkMode
- if rootless.IsRootless() {
- netmode = namespaces.NetworkMode(specgen.Slirp)
- }
-
- network := createconfig.NetworkConfig{
- DNSOpt: input.HostConfig.DNSOptions,
- DNSSearch: input.HostConfig.DNSSearch,
- DNSServers: input.HostConfig.DNS,
- ExposedPorts: input.ExposedPorts,
- HTTPProxy: false, // podman
- IP6Address: "",
- IPAddress: "",
- LinkLocalIP: nil, // docker-only
- MacAddress: input.MacAddress,
- NetMode: netmode,
- Network: input.HostConfig.NetworkMode.NetworkName(),
- NetworkAlias: nil, // docker-only now
- PortBindings: input.HostConfig.PortBindings,
- Publish: nil, // podmanseccompPath
- PublishAll: input.HostConfig.PublishAllPorts,
- }
-
- uts := createconfig.UtsConfig{
- UtsMode: namespaces.UTSMode(input.HostConfig.UTSMode),
- NoHosts: false, //podman
- HostAdd: input.HostConfig.ExtraHosts,
- Hostname: input.Hostname,
- }
-
- z := createconfig.UserConfig{
- GroupAdd: input.HostConfig.GroupAdd,
- IDMappings: &storage.IDMappingOptions{}, // podman //TODO <--- fix this,
- UsernsMode: namespaces.UsernsMode(input.HostConfig.UsernsMode),
- User: input.User,
- }
- pidConfig := createconfig.PidConfig{PidMode: namespaces.PidMode(input.HostConfig.PidMode)}
- // TODO: We should check that these binds are all listed in the `Volumes`
- // key since it doesn't make sense to define a `Binds` element for a
- // container path which isn't defined as a volume
- volumes := input.HostConfig.Binds
-
- // Docker is more flexible about its input where podman throws
- // away incorrectly formatted variables so we cannot reuse the
- // parsing of the env input
- // [Foo Other=one Blank=]
- imgEnv, err := newImage.Env(ctx)
- if err != nil {
- return createconfig.CreateConfig{}, err
- }
- input.Env = append(imgEnv, input.Env...)
- for _, e := range input.Env {
- splitEnv := strings.Split(e, "=")
- switch len(splitEnv) {
- case 0:
- continue
- case 1:
- env[splitEnv[0]] = ""
- default:
- env[splitEnv[0]] = strings.Join(splitEnv[1:], "=")
- }
- }
-
- // format the tmpfs mounts into a []string from map
- tmpfs := make([]string, 0, len(input.HostConfig.Tmpfs))
- for k, v := range input.HostConfig.Tmpfs {
- tmpfs = append(tmpfs, fmt.Sprintf("%s:%s", k, v))
- }
-
- if input.HostConfig.Init != nil && *input.HostConfig.Init {
- init = true
- }
-
- m := createconfig.CreateConfig{
- Annotations: nil, // podman
- Args: nil,
- Cgroup: c,
- CidFile: "",
- ConmonPidFile: "", // podman
- Command: input.Cmd,
- UserCommand: input.Cmd, // podman
- Detach: false, //
- // Devices: input.HostConfig.Devices,
- Entrypoint: input.Entrypoint,
- Env: env,
- HealthCheck: nil, //
- Init: init,
- InitPath: "", // tbd
- Image: input.Image,
- ImageID: newImage.ID(),
- BuiltinImgVolumes: nil, // podman
- ImageVolumeType: "", // podman
- Interactive: input.OpenStdin,
- // IpcMode: input.HostConfig.IpcMode,
- Labels: input.Labels,
- LogDriver: input.HostConfig.LogConfig.Type, // is this correct
- // LogDriverOpt: input.HostConfig.LogConfig.Config,
- Name: input.Name,
- Network: network,
- Pod: "", // podman
- PodmanPath: "", // podman
- Quiet: false, // front-end only
- Resources: createconfig.CreateResourceConfig{},
- RestartPolicy: input.HostConfig.RestartPolicy.Name,
- Rm: input.HostConfig.AutoRemove,
- StopSignal: stopSignal,
- StopTimeout: stopTimeout,
- Systemd: false, // podman
- Tmpfs: tmpfs,
- User: z,
- Uts: uts,
- Tty: input.Tty,
- Mounts: nil, // we populate
- // MountsFlag: input.HostConfig.Mounts,
- NamedVolumes: nil, // we populate
- Volumes: volumes,
- VolumesFrom: input.HostConfig.VolumesFrom,
- WorkDir: workDir,
- Rootfs: "", // podman
- Security: security,
- Syslog: false, // podman
-
- Pid: pidConfig,
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "container create"))
+ return
}
-
- fullCmd := append(input.Entrypoint, input.Cmd...)
- if len(fullCmd) > 0 {
- m.PodmanPath = fullCmd[0]
- if len(fullCmd) == 1 {
- m.Args = fullCmd
- } else {
- m.Args = fullCmd[1:]
- }
+ createResponse := entities.ContainerCreateResponse{
+ ID: report.Id,
+ Warnings: []string{},
}
-
- return m, nil
+ utils.WriteResponse(w, http.StatusCreated, createResponse)
}
diff --git a/pkg/api/handlers/compat/containers_logs.go b/pkg/api/handlers/compat/containers_logs.go
index faab66fe7..38a6329b9 100644
--- a/pkg/api/handlers/compat/containers_logs.go
+++ b/pkg/api/handlers/compat/containers_logs.go
@@ -148,7 +148,13 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) {
frame.WriteString(line.Time.Format(time.RFC3339))
frame.WriteString(" ")
}
+
frame.WriteString(line.Msg)
+ // Log lines in the compat layer require adding EOL
+ // https://github.com/containers/podman/issues/8058
+ if !utils.IsLibpodRequest(r) {
+ frame.WriteString("\n")
+ }
if writeHeader {
binary.BigEndian.PutUint32(header[4:], uint32(frame.Len()))
diff --git a/pkg/api/handlers/compat/events.go b/pkg/api/handlers/compat/events.go
index a729b84d4..f74491a8f 100644
--- a/pkg/api/handlers/compat/events.go
+++ b/pkg/api/handlers/compat/events.go
@@ -112,11 +112,15 @@ func GetEvents(w http.ResponseWriter, r *http.Request) {
errorChannel <- runtime.Events(r.Context(), readOpts)
}()
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusOK)
+ var flush = func() {}
if flusher, ok := w.(http.Flusher); ok {
- flusher.Flush()
+ flush = flusher.Flush
}
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ flush()
+
coder := json.NewEncoder(w)
coder.SetEscapeHTML(true)
@@ -124,6 +128,7 @@ func GetEvents(w http.ResponseWriter, r *http.Request) {
select {
case err := <-errorChannel:
if err != nil {
+ // FIXME StatusOK already sent above cannot send 500 here
utils.InternalServerError(w, err)
return
}
@@ -136,9 +141,7 @@ func GetEvents(w http.ResponseWriter, r *http.Request) {
if err := coder.Encode(e); err != nil {
logrus.Errorf("unable to write json: %q", err)
}
- if flusher, ok := w.(http.Flusher); ok {
- flusher.Flush()
- }
+ flush()
case <-r.Context().Done():
return
}
diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go
index 3431823bd..d177b2335 100644
--- a/pkg/api/handlers/compat/images.go
+++ b/pkg/api/handlers/compat/images.go
@@ -60,7 +60,7 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile"))
return
}
- if err := newImage.Save(r.Context(), name, "docker-archive", tmpfile.Name(), []string{}, false, false); err != nil {
+ if err := newImage.Save(r.Context(), name, "docker-archive", tmpfile.Name(), []string{}, false, false, true); err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to save image"))
return
}
@@ -429,7 +429,7 @@ func ExportImages(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile"))
return
}
- if err := runtime.ImageRuntime().SaveImages(r.Context(), images, "docker-archive", tmpfile.Name(), false); err != nil {
+ if err := runtime.ImageRuntime().SaveImages(r.Context(), images, "docker-archive", tmpfile.Name(), false, true); err != nil {
utils.InternalServerError(w, err)
return
}
diff --git a/pkg/api/handlers/compat/images_search.go b/pkg/api/handlers/compat/images_search.go
index 131fab69f..b3ceae3ee 100644
--- a/pkg/api/handlers/compat/images_search.go
+++ b/pkg/api/handlers/compat/images_search.go
@@ -7,6 +7,7 @@ import (
"github.com/containers/image/v5/types"
"github.com/containers/podman/v2/libpod/image"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
+ "github.com/containers/podman/v2/pkg/auth"
"github.com/gorilla/schema"
"github.com/pkg/errors"
)
@@ -14,9 +15,10 @@ import (
func SearchImages(w http.ResponseWriter, r *http.Request) {
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
- Term string `json:"term"`
- Limit int `json:"limit"`
- Filters map[string][]string `json:"filters"`
+ Term string `json:"term"`
+ Limit int `json:"limit"`
+ Filters map[string][]string `json:"filters"`
+ TLSVerify bool `json:"tlsVerify"`
}{
// This is where you can override the golang default value for one of fields
}
@@ -58,6 +60,18 @@ func SearchImages(w http.ResponseWriter, r *http.Request) {
Limit: query.Limit,
}
+ if _, found := r.URL.Query()["tlsVerify"]; found {
+ options.InsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
+ }
+
+ _, authfile, key, err := auth.GetCredentials(r)
+ if err != nil {
+ utils.Error(w, "failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "failed to parse %q header for %s", key, r.URL.String()))
+ return
+ }
+ defer auth.RemoveAuthfile(authfile)
+ options.Authfile = authfile
+
results, err := image.SearchImages(query.Term, options)
if err != nil {
utils.BadRequest(w, "term", query.Term, err)
diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go
index a46784a6c..8011c0a04 100644
--- a/pkg/api/handlers/compat/networks.go
+++ b/pkg/api/handlers/compat/networks.go
@@ -20,6 +20,7 @@ import (
dockerNetwork "github.com/docker/docker/api/types/network"
"github.com/gorilla/schema"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
func InspectNetwork(w http.ResponseWriter, r *http.Request) {
@@ -231,6 +232,9 @@ func CreateNetwork(w http.ResponseWriter, r *http.Request) {
if len(networkCreate.Name) > 0 {
name = networkCreate.Name
}
+ if len(networkCreate.Driver) < 1 {
+ networkCreate.Driver = network.DefaultNetworkDriver
+ }
// At present I think we should just support the bridge driver
// and allow demand to make us consider more
if networkCreate.Driver != network.DefaultNetworkDriver {
@@ -312,3 +316,81 @@ func RemoveNetwork(w http.ResponseWriter, r *http.Request) {
}
utils.WriteResponse(w, http.StatusNoContent, "")
}
+
+// Connect adds a container to a network
+// TODO: For now this func is a no-op that checks the container name, network name, and
+// responds with a 200. This allows the call to remain intact. We need to decide how
+// we make this work with CNI networking and setup/teardown.
+func Connect(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+
+ var netConnect types.NetworkConnect
+ if err := json.NewDecoder(r.Body).Decode(&netConnect); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
+ return
+ }
+ config, err := runtime.GetConfig()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ name := utils.GetName(r)
+ exists, err := network.Exists(config, name)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ if !exists {
+ utils.Error(w, "network not found", http.StatusNotFound, define.ErrNoSuchNetwork)
+ return
+ }
+ if _, err = runtime.LookupContainer(netConnect.Container); err != nil {
+ if errors.Cause(err) == define.ErrNoSuchCtr {
+ utils.ContainerNotFound(w, netConnect.Container, err)
+ return
+ }
+ utils.Error(w, "unable to lookup container", http.StatusInternalServerError, err)
+ return
+ }
+ logrus.Warnf("network connect endpoint is not fully implemented - tried to connect container %s to network %s", netConnect.Container, name)
+ utils.WriteResponse(w, http.StatusOK, "OK")
+}
+
+// Disconnect removes a container from a network
+// TODO: For now this func is a no-op that checks the container name, network name, and
+// responds with a 200. This allows the call to remain intact. We need to decide how
+// we make this work with CNI networking and setup/teardown.
+func Disconnect(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+
+ var netDisconnect types.NetworkDisconnect
+ if err := json.NewDecoder(r.Body).Decode(&netDisconnect); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
+ return
+ }
+ config, err := runtime.GetConfig()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ name := utils.GetName(r)
+ exists, err := network.Exists(config, name)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ if !exists {
+ utils.Error(w, "network not found", http.StatusNotFound, define.ErrNoSuchNetwork)
+ return
+ }
+ if _, err = runtime.LookupContainer(netDisconnect.Container); err != nil {
+ if errors.Cause(err) == define.ErrNoSuchCtr {
+ utils.ContainerNotFound(w, netDisconnect.Container, err)
+ return
+ }
+ utils.Error(w, "unable to lookup container", http.StatusInternalServerError, err)
+ return
+ }
+ logrus.Warnf("network disconnect endpoint is not fully implemented - tried to connect container %s to network %s", netDisconnect.Container, name)
+ utils.WriteResponse(w, http.StatusOK, "OK")
+}
diff --git a/pkg/api/handlers/compat/swagger.go b/pkg/api/handlers/compat/swagger.go
index 371d02388..0a514822b 100644
--- a/pkg/api/handlers/compat/swagger.go
+++ b/pkg/api/handlers/compat/swagger.go
@@ -63,3 +63,17 @@ type swagCompatNetworkCreateResponse struct {
// in:body
Body struct{ types.NetworkCreate }
}
+
+// Network disconnect
+// swagger:model NetworkConnectRequest
+type swagCompatNetworkConnectRequest struct {
+ // in:body
+ Body struct{ types.NetworkConnect }
+}
+
+// Network disconnect
+// swagger:model NetworkDisconnectRequest
+type swagCompatNetworkDisconnectRequest struct {
+ // in:body
+ Body struct{ types.NetworkDisconnect }
+}
diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go
index 7dde51102..7e6481321 100644
--- a/pkg/api/handlers/libpod/containers.go
+++ b/pkg/api/handlers/libpod/containers.go
@@ -11,6 +11,7 @@ import (
"github.com/containers/podman/v2/pkg/api/handlers/compat"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
"github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/containers/podman/v2/pkg/domain/infra/abi"
"github.com/containers/podman/v2/pkg/ps"
"github.com/gorilla/schema"
"github.com/pkg/errors"
@@ -18,9 +19,30 @@ import (
)
func ContainerExists(w http.ResponseWriter, r *http.Request) {
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ // Now use the ABI implementation to prevent us from having duplicate
+ // code.
+ containerEngine := abi.ContainerEngine{Libpod: runtime}
+
name := utils.GetName(r)
- _, err := runtime.LookupContainer(name)
+ query := struct {
+ External bool `schema:"external"`
+ }{
+ // override any golang type defaults
+ }
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+
+ options := entities.ContainerExistsOptions{
+ External: query.External,
+ }
+
+ report, err := containerEngine.ContainerExists(r.Context(), name, options)
if err != nil {
if errors.Cause(err) == define.ErrNoSuchCtr {
utils.ContainerNotFound(w, name, err)
@@ -30,7 +52,11 @@ func ContainerExists(w http.ResponseWriter, r *http.Request) {
return
}
- utils.WriteResponse(w, http.StatusNoContent, "")
+ if report.Value {
+ utils.WriteResponse(w, http.StatusNoContent, "")
+ } else {
+ utils.ContainerNotFound(w, name, define.ErrNoSuchCtr)
+ }
}
func ListContainers(w http.ResponseWriter, r *http.Request) {
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index 1292090fb..598a46abe 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -206,7 +206,7 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "unknown format", http.StatusInternalServerError, errors.Errorf("unknown format %q", query.Format))
return
}
- if err := newImage.Save(r.Context(), name, query.Format, output, []string{}, false, query.Compress); err != nil {
+ if err := newImage.Save(r.Context(), name, query.Format, output, []string{}, false, query.Compress, true); err != nil {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err)
return
}
@@ -284,6 +284,7 @@ func ExportImages(w http.ResponseWriter, r *http.Request) {
Format: query.Format,
MultiImageArchive: true,
Output: output,
+ RemoveSignatures: true,
}
imageEngine := abi.ImageEngine{Libpod: runtime}
@@ -636,6 +637,14 @@ func SearchImages(w http.ResponseWriter, r *http.Request) {
options.Filter = *filter
}
+ _, authfile, key, err := auth.GetCredentials(r)
+ if err != nil {
+ utils.Error(w, "failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "failed to parse %q header for %s", key, r.URL.String()))
+ return
+ }
+ defer auth.RemoveAuthfile(authfile)
+ options.Authfile = authfile
+
searchResults, err := image.SearchImages(query.Term, options)
if err != nil {
utils.BadRequest(w, "term", query.Term, err)
diff --git a/pkg/api/handlers/libpod/networks.go b/pkg/api/handlers/libpod/networks.go
index b7e2b3988..9f6103c45 100644
--- a/pkg/api/handlers/libpod/networks.go
+++ b/pkg/api/handlers/libpod/networks.go
@@ -113,15 +113,15 @@ func InspectNetwork(w http.ResponseWriter, r *http.Request) {
return
}
name := utils.GetName(r)
- options := entities.NetworkInspectOptions{}
+ options := entities.InspectOptions{}
ic := abi.ContainerEngine{Libpod: runtime}
- reports, err := ic.NetworkInspect(r.Context(), []string{name}, options)
+ reports, errs, err := ic.NetworkInspect(r.Context(), []string{name}, options)
+ // If the network cannot be found, we return a 404.
+ if len(errs) > 0 {
+ utils.Error(w, "Something went wrong", http.StatusNotFound, define.ErrNoSuchNetwork)
+ return
+ }
if err != nil {
- // If the network cannot be found, we return a 404.
- if errors.Cause(err) == define.ErrNoSuchNetwork {
- utils.Error(w, "Something went wrong", http.StatusNotFound, err)
- return
- }
utils.InternalServerError(w, err)
return
}
diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go
index 70466f01b..6bb5f5101 100644
--- a/pkg/api/handlers/types.go
+++ b/pkg/api/handlers/types.go
@@ -271,6 +271,15 @@ func ImageDataToImageInspect(ctx context.Context, l *libpodImage.Image) (*ImageI
if err != nil {
return nil, err
}
+
+ rootfs := docker.RootFS{}
+ if info.RootFS != nil {
+ rootfs.Type = info.RootFS.Type
+ rootfs.Layers = make([]string, 0, len(info.RootFS.Layers))
+ for _, layer := range info.RootFS.Layers {
+ rootfs.Layers = append(rootfs.Layers, string(layer))
+ }
+ }
dockerImageInspect := docker.ImageInspect{
Architecture: l.Architecture,
Author: l.Author,
@@ -286,7 +295,7 @@ func ImageDataToImageInspect(ctx context.Context, l *libpodImage.Image) (*ImageI
Parent: l.Parent,
RepoDigests: info.RepoDigests,
RepoTags: info.RepoTags,
- RootFS: docker.RootFS{},
+ RootFS: rootfs,
Size: info.Size,
Variant: "",
VirtualSize: info.VirtualSize,
diff --git a/pkg/api/server/register_networks.go b/pkg/api/server/register_networks.go
index 61916eedf..6222006e5 100644
--- a/pkg/api/server/register_networks.go
+++ b/pkg/api/server/register_networks.go
@@ -98,6 +98,63 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
// $ref: "#/responses/InternalError"
r.HandleFunc(VersionedPath("/networks/create"), s.APIHandler(compat.CreateNetwork)).Methods(http.MethodPost)
r.HandleFunc("/networks/create", s.APIHandler(compat.CreateNetwork)).Methods(http.MethodPost)
+ // swagger:operation POST /networks/{name}/connect compat compatConnectNetwork
+ // ---
+ // tags:
+ // - networks (compat)
+ // summary: Connect container to network
+ // description: Connect a container to a network. This endpoint is current a no-op
+ // produces:
+ // - application/json
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name of the network
+ // - in: body
+ // name: create
+ // description: attributes for connecting a container to a network
+ // schema:
+ // $ref: "#/definitions/NetworkConnectRequest"
+ // responses:
+ // 200:
+ // description: OK
+ // 400:
+ // $ref: "#/responses/BadParamError"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/networks/{name}/connect"), s.APIHandler(compat.Connect)).Methods(http.MethodPost)
+ r.HandleFunc("/networks/{name}/connect", s.APIHandler(compat.Connect)).Methods(http.MethodPost)
+ // swagger:operation POST /networks/{name}/disconnect compat compatDisconnectNetwork
+ // ---
+ // tags:
+ // - networks (compat)
+ // summary: Disconnect container from network
+ // description: Disconnect a container from a network. This endpoint is current a no-op
+ // produces:
+ // - application/json
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name of the network
+ // - in: body
+ // name: create
+ // description: attributes for disconnecting a container from a network
+ // schema:
+ // $ref: "#/definitions/NetworkDisconnectRequest"
+ // responses:
+ // 200:
+ // description: OK
+ // 400:
+ // $ref: "#/responses/BadParamError"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/networks/{name}/disconnect"), s.APIHandler(compat.Disconnect)).Methods(http.MethodPost)
+ r.HandleFunc("/networks/{name}/disconnect", s.APIHandler(compat.Disconnect)).Methods(http.MethodPost)
+
// swagger:operation DELETE /libpod/networks/{name} libpod libpodRemoveNetwork
// ---
// tags:
diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go
index 355a46fb7..64008767b 100644
--- a/pkg/api/server/server.go
+++ b/pkg/api/server/server.go
@@ -7,7 +7,6 @@ import (
"net"
"net/http"
"os"
- "os/signal"
goRuntime "runtime"
"strings"
"sync"
@@ -15,6 +14,7 @@ import (
"time"
"github.com/containers/podman/v2/libpod"
+ "github.com/containers/podman/v2/libpod/shutdown"
"github.com/containers/podman/v2/pkg/api/handlers"
"github.com/containers/podman/v2/pkg/api/server/idle"
"github.com/coreos/go-systemd/v22/activation"
@@ -180,8 +180,17 @@ func setupSystemd() {
// Serve starts responding to HTTP requests.
func (s *APIServer) Serve() error {
setupSystemd()
- sigChan := make(chan os.Signal, 1)
- signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
+
+ // Start the shutdown signal handler.
+ if err := shutdown.Start(); err != nil {
+ return err
+ }
+ if err := shutdown.Register("server", func(sig os.Signal) error {
+ return s.Shutdown()
+ }); err != nil {
+ return err
+ }
+
errChan := make(chan error, 1)
go func() {
@@ -217,14 +226,7 @@ func (s *APIServer) Serve() error {
errChan <- nil
}()
- select {
- case err := <-errChan:
- return err
- case sig := <-sigChan:
- logrus.Infof("APIServer terminated by signal %v", sig)
- }
-
- return nil
+ return <-errChan
}
// Shutdown is a clean shutdown waiting on existing clients
diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go
index 3a7662c41..31435ae91 100644
--- a/pkg/bindings/connection.go
+++ b/pkg/bindings/connection.go
@@ -214,19 +214,23 @@ func sshClient(_url *url.URL, secure bool, passPhrase string, identity string) (
authMethods = append(authMethods, ssh.Password(string(pass)))
}
+ port := _url.Port()
+ if port == "" {
+ port = "22"
+ }
+
callback := ssh.InsecureIgnoreHostKey()
if secure {
- key := terminal.HostKey(_url.Hostname())
+ host := _url.Hostname()
+ if port != "22" {
+ host = fmt.Sprintf("[%s]:%s", host, port)
+ }
+ key := terminal.HostKey(host)
if key != nil {
callback = ssh.FixedHostKey(key)
}
}
- port := _url.Port()
- if port == "" {
- port = "22"
- }
-
bastion, err := ssh.Dial("tcp",
net.JoinHostPort(_url.Hostname(), port),
&ssh.ClientConfig{
diff --git a/pkg/bindings/containers/attach.go b/pkg/bindings/containers/attach.go
index 3bd85fbae..7b321af93 100644
--- a/pkg/bindings/containers/attach.go
+++ b/pkg/bindings/containers/attach.go
@@ -19,6 +19,7 @@ import (
"github.com/containers/podman/v2/pkg/bindings"
sig "github.com/containers/podman/v2/pkg/signal"
"github.com/containers/podman/v2/utils"
+ "github.com/moby/term"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh/terminal"
@@ -60,8 +61,14 @@ func Attach(ctx context.Context, nameOrID string, detachKeys *string, logs, stre
}
params := url.Values{}
+ detachKeysInBytes := []byte{}
if detachKeys != nil {
params.Add("detachKeys", *detachKeys)
+
+ detachKeysInBytes, err = term.ToBytes(*detachKeys)
+ if err != nil {
+ return errors.Wrapf(err, "invalid detach keys")
+ }
}
if logs != nil {
params.Add("logs", fmt.Sprintf("%t", *logs))
@@ -141,27 +148,51 @@ func Attach(ctx context.Context, nameOrID string, detachKeys *string, logs, stre
attachReady <- true
}
+ stdoutChan := make(chan error)
+ stdinChan := make(chan error)
+
if isSet.stdin {
go func() {
logrus.Debugf("Copying STDIN to socket")
- _, err := utils.CopyDetachable(socket, stdin, []byte{})
- if err != nil {
+
+ _, err := utils.CopyDetachable(socket, stdin, detachKeysInBytes)
+
+ if err != nil && err != define.ErrDetach {
logrus.Error("failed to write input to service: " + err.Error())
}
+ stdinChan <- err
}()
}
buffer := make([]byte, 1024)
if ctnr.Config.Tty {
- logrus.Debugf("Copying STDOUT of container in terminal mode")
+ go func() {
+ logrus.Debugf("Copying STDOUT of container in terminal mode")
- if !isSet.stdout {
- return fmt.Errorf("container %q requires stdout to be set", ctnr.ID)
- }
- // If not multiplex'ed, read from server and write to stdout
- _, err := io.Copy(stdout, socket)
- if err != nil {
- return err
+ if !isSet.stdout {
+ stdoutChan <- fmt.Errorf("container %q requires stdout to be set", ctnr.ID)
+ }
+ // If not multiplex'ed, read from server and write to stdout
+ _, err := io.Copy(stdout, socket)
+
+ stdoutChan <- err
+ }()
+
+ for {
+ select {
+ case err := <-stdoutChan:
+ if err != nil {
+ return err
+ }
+
+ return nil
+ case err := <-stdinChan:
+ if err != nil {
+ return err
+ }
+
+ return nil
+ }
}
} else {
logrus.Debugf("Copying standard streams of container in non-terminal mode")
@@ -205,7 +236,6 @@ func Attach(ctx context.Context, nameOrID string, detachKeys *string, logs, stre
}
}
}
- return nil
}
// DemuxHeader reads header for stream from server multiplexed stdin/stdout/stderr/2nd error channel
diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go
index 708ad06cb..b5cd2128b 100644
--- a/pkg/bindings/containers/containers.go
+++ b/pkg/bindings/containers/containers.go
@@ -322,12 +322,14 @@ func Wait(ctx context.Context, nameOrID string, condition *define.ContainerStatu
// Exists is a quick, light-weight way to determine if a given container
// exists in local storage. The nameOrID can be a container name
// or a partial/full ID.
-func Exists(ctx context.Context, nameOrID string) (bool, error) {
+func Exists(ctx context.Context, nameOrID string, external bool) (bool, error) {
conn, err := bindings.GetClient(ctx)
if err != nil {
return false, err
}
- response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/exists", nil, nil, nameOrID)
+ params := url.Values{}
+ params.Set("external", strconv.FormatBool(external))
+ response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/exists", params, nil, nameOrID)
if err != nil {
return false, err
}
diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go
index 408b4769d..0fb677768 100644
--- a/pkg/bindings/test/containers_test.go
+++ b/pkg/bindings/test/containers_test.go
@@ -405,7 +405,7 @@ var _ = Describe("Podman containers ", func() {
It("podman bogus container does not exist in local storage", func() {
// Bogus container existence check should fail
- containerExists, err := containers.Exists(bt.conn, "foobar")
+ containerExists, err := containers.Exists(bt.conn, "foobar", false)
Expect(err).To(BeNil())
Expect(containerExists).To(BeFalse())
})
@@ -415,7 +415,7 @@ var _ = Describe("Podman containers ", func() {
var name = "top"
_, err := bt.RunTopContainer(&name, bindings.PFalse, nil)
Expect(err).To(BeNil())
- containerExists, err := containers.Exists(bt.conn, name)
+ containerExists, err := containers.Exists(bt.conn, name, false)
Expect(err).To(BeNil())
Expect(containerExists).To(BeTrue())
})
@@ -425,7 +425,7 @@ var _ = Describe("Podman containers ", func() {
var name = "top"
cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil)
Expect(err).To(BeNil())
- containerExists, err := containers.Exists(bt.conn, cid)
+ containerExists, err := containers.Exists(bt.conn, cid, false)
Expect(err).To(BeNil())
Expect(containerExists).To(BeTrue())
})
@@ -435,7 +435,7 @@ var _ = Describe("Podman containers ", func() {
var name = "top"
cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil)
Expect(err).To(BeNil())
- containerExists, err := containers.Exists(bt.conn, cid[0:12])
+ containerExists, err := containers.Exists(bt.conn, cid[0:12], false)
Expect(err).To(BeNil())
Expect(containerExists).To(BeTrue())
})
@@ -455,7 +455,7 @@ var _ = Describe("Podman containers ", func() {
Expect(err).To(BeNil())
err = containers.Kill(bt.conn, name, "SIGINT")
Expect(err).To(BeNil())
- _, err = containers.Exists(bt.conn, name)
+ _, err = containers.Exists(bt.conn, name, false)
Expect(err).To(BeNil())
})
@@ -466,7 +466,7 @@ var _ = Describe("Podman containers ", func() {
Expect(err).To(BeNil())
err = containers.Kill(bt.conn, cid, "SIGTERM")
Expect(err).To(BeNil())
- _, err = containers.Exists(bt.conn, cid)
+ _, err = containers.Exists(bt.conn, cid, false)
Expect(err).To(BeNil())
})
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
index 3b6dd106f..3fd7c79f4 100644
--- a/pkg/domain/entities/containers.go
+++ b/pkg/domain/entities/containers.go
@@ -246,6 +246,11 @@ type ExecOptions struct {
WorkDir string
}
+// ContainerExistsOptions describes the cli values to check if a container exists
+type ContainerExistsOptions struct {
+ External bool
+}
+
// ContainerStartOptions describes the val from the
// CLI needed to start a container
type ContainerStartOptions struct {
@@ -289,6 +294,7 @@ type ContainerListOptions struct {
// ContainerRunOptions describes the options needed
// to run a container from the CLI
type ContainerRunOptions struct {
+ CIDFile string
Detach bool
DetachKeys string
ErrorStream *os.File
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index 803a59932..8ab72dbd8 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -21,7 +21,7 @@ type ContainerEngine interface {
ContainerDiff(ctx context.Context, nameOrID string, options DiffOptions) (*DiffReport, error)
ContainerExec(ctx context.Context, nameOrID string, options ExecOptions, streams define.AttachStreams) (int, error)
ContainerExecDetached(ctx context.Context, nameOrID string, options ExecOptions) (string, error)
- ContainerExists(ctx context.Context, nameOrID string) (*BoolReport, error)
+ ContainerExists(ctx context.Context, nameOrID string, options ContainerExistsOptions) (*BoolReport, error)
ContainerExport(ctx context.Context, nameOrID string, options ContainerExportOptions) error
ContainerInit(ctx context.Context, namesOrIds []string, options ContainerInitOptions) ([]*ContainerInitReport, error)
ContainerInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]*ContainerInspectReport, []error, error)
@@ -51,7 +51,7 @@ type ContainerEngine interface {
HealthCheckRun(ctx context.Context, nameOrID string, options HealthCheckOptions) (*define.HealthCheckResults, error)
Info(ctx context.Context) (*define.Info, error)
NetworkCreate(ctx context.Context, name string, options NetworkCreateOptions) (*NetworkCreateReport, error)
- NetworkInspect(ctx context.Context, namesOrIds []string, options NetworkInspectOptions) ([]NetworkInspectReport, error)
+ NetworkInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]NetworkInspectReport, []error, error)
NetworkList(ctx context.Context, options NetworkListOptions) ([]*NetworkListReport, error)
NetworkRm(ctx context.Context, namesOrIds []string, options NetworkRmOptions) ([]*NetworkRmReport, error)
PlayKube(ctx context.Context, path string, opts PlayKubeOptions) (*PlayKubeReport, error)
@@ -76,7 +76,7 @@ type ContainerEngine interface {
VarlinkService(ctx context.Context, opts ServiceOptions) error
Version(ctx context.Context) (*SystemVersionReport, error)
VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IDOrNameResponse, error)
- VolumeInspect(ctx context.Context, namesOrIds []string, opts VolumeInspectOptions) ([]*VolumeInspectReport, error)
+ VolumeInspect(ctx context.Context, namesOrIds []string, opts InspectOptions) ([]*VolumeInspectReport, []error, error)
VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error)
VolumePrune(ctx context.Context) ([]*VolumePruneReport, error)
VolumeRm(ctx context.Context, namesOrIds []string, opts VolumeRmOptions) ([]*VolumeRmReport, error)
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index 982fa0cc0..101542a98 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -300,6 +300,8 @@ type ImageSaveOptions struct {
MultiImageArchive bool
// Output - write image to the specified path.
Output string
+ // Do not save the signature from the source image
+ RemoveSignatures bool
// Quiet - suppress output when copying images
Quiet bool
}
diff --git a/pkg/domain/entities/network.go b/pkg/domain/entities/network.go
index 9beeeb042..0bab672a7 100644
--- a/pkg/domain/entities/network.go
+++ b/pkg/domain/entities/network.go
@@ -18,11 +18,6 @@ type NetworkListReport struct {
*libcni.NetworkConfigList
}
-// NetworkInspectOptions describes options for inspect networks
-type NetworkInspectOptions struct {
- Format string
-}
-
// NetworkInspectReport describes the results from inspect networks
type NetworkInspectReport map[string]interface{}
diff --git a/pkg/domain/entities/types.go b/pkg/domain/entities/types.go
index 5fdd8afcb..d8ad2d891 100644
--- a/pkg/domain/entities/types.go
+++ b/pkg/domain/entities/types.go
@@ -56,6 +56,8 @@ type InspectOptions struct {
Size bool `json:",omitempty"`
// Type -- return JSON for specified type.
Type string `json:",omitempty"`
+ // All -- inspect all
+ All bool `json:",omitempty"`
}
// All API and CLI diff commands and diff sub-commands use the same options
diff --git a/pkg/domain/entities/volumes.go b/pkg/domain/entities/volumes.go
index fb8466d04..1bc1e4301 100644
--- a/pkg/domain/entities/volumes.go
+++ b/pkg/domain/entities/volumes.go
@@ -105,10 +105,6 @@ type VolumeRmReport struct {
Id string //nolint
}
-type VolumeInspectOptions struct {
- All bool
-}
-
type VolumeInspectReport struct {
*VolumeConfigResponse
}
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index 614fd5fe0..98b886845 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -29,6 +29,7 @@ import (
"github.com/containers/podman/v2/pkg/signal"
"github.com/containers/podman/v2/pkg/specgen"
"github.com/containers/podman/v2/pkg/specgen/generate"
+ "github.com/containers/podman/v2/pkg/util"
"github.com/containers/storage"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -74,11 +75,19 @@ func getContainersByContext(all, latest bool, names []string, runtime *libpod.Ru
return
}
-// TODO: Should return *entities.ContainerExistsReport, error
-func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string) (*entities.BoolReport, error) {
+// ContainerExists returns whether the container exists in container storage
+func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string, options entities.ContainerExistsOptions) (*entities.BoolReport, error) {
_, err := ic.Libpod.LookupContainer(nameOrID)
- if err != nil && errors.Cause(err) != define.ErrNoSuchCtr {
- return nil, err
+ if err != nil {
+ if errors.Cause(err) != define.ErrNoSuchCtr {
+ return nil, err
+ }
+ if options.External {
+ // Check if container exists in storage
+ if _, storageErr := ic.Libpod.StorageContainer(nameOrID); storageErr == nil {
+ err = nil
+ }
+ }
}
return &entities.BoolReport{Value: err == nil}, nil
}
@@ -838,6 +847,12 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
return nil, err
}
+ if opts.CIDFile != "" {
+ if err := util.CreateCidFile(opts.CIDFile, ctr.ID()); err != nil {
+ return nil, err
+ }
+ }
+
var joinPod bool
if len(ctr.PodID()) > 0 {
joinPod = true
diff --git a/pkg/domain/infra/abi/containers_runlabel.go b/pkg/domain/infra/abi/containers_runlabel.go
index 30a5a55b8..41fdf8f34 100644
--- a/pkg/domain/infra/abi/containers_runlabel.go
+++ b/pkg/domain/infra/abi/containers_runlabel.go
@@ -28,6 +28,9 @@ func (ic *ContainerEngine) ContainerRunlabel(ctx context.Context, label string,
if err != nil {
return err
}
+ if runlabel == "" {
+ return errors.Errorf("cannot find the value of label: %s in image: %s", label, imageRef)
+ }
cmd, env, err := generateRunlabelCommand(runlabel, img, args, options)
if err != nil {
diff --git a/pkg/domain/infra/abi/cp.go b/pkg/domain/infra/abi/cp.go
index a0bfcc90c..ab90c8183 100644
--- a/pkg/domain/infra/abi/cp.go
+++ b/pkg/domain/infra/abi/cp.go
@@ -26,7 +26,7 @@ import (
)
func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) (*entities.ContainerCpReport, error) {
- var extract bool
+ extract := options.Extract
srcCtr, srcPath := parsePath(ic.Libpod, source)
destCtr, destPath := parsePath(ic.Libpod, dest)
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index f9d733c63..25335cf11 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -482,13 +482,13 @@ func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOpti
func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, options entities.ImageSaveOptions) error {
if options.MultiImageArchive {
nameOrIDs := append([]string{nameOrID}, tags...)
- return ir.Libpod.ImageRuntime().SaveImages(ctx, nameOrIDs, options.Format, options.Output, options.Quiet)
+ return ir.Libpod.ImageRuntime().SaveImages(ctx, nameOrIDs, options.Format, options.Output, options.Quiet, true)
}
newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID)
if err != nil {
return err
}
- return newImage.Save(ctx, nameOrID, options.Format, options.Output, tags, options.Quiet, options.Compress)
+ return newImage.Save(ctx, nameOrID, options.Format, options.Output, tags, options.Quiet, options.Compress, true)
}
func (ir *ImageEngine) Diff(_ context.Context, nameOrID string, _ entities.DiffOptions) (*entities.DiffReport, error) {
diff --git a/pkg/domain/infra/abi/images_list.go b/pkg/domain/infra/abi/images_list.go
index 3e47dc67a..281b04294 100644
--- a/pkg/domain/infra/abi/images_list.go
+++ b/pkg/domain/infra/abi/images_list.go
@@ -5,6 +5,7 @@ import (
libpodImage "github.com/containers/podman/v2/libpod/image"
"github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/pkg/errors"
)
func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) ([]*entities.ImageSummary, error) {
@@ -43,12 +44,21 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions)
VirtualSize: img.VirtualSize,
RepoTags: img.Names(), // may include tags and digests
}
- e.Labels, _ = img.Labels(context.TODO())
+ e.Labels, err = img.Labels(ctx)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error retrieving label for image %q: you may need to remove the image to resolve the error", img.ID())
+ }
- ctnrs, _ := img.Containers()
+ ctnrs, err := img.Containers()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error retrieving containers for image %q: you may need to remove the image to resolve the error", img.ID())
+ }
e.Containers = len(ctnrs)
- sz, _ := img.Size(context.TODO())
+ sz, err := img.Size(ctx)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error retrieving size of image %q: you may need to remove the image to resolve the error", img.ID())
+ }
e.Size = int64(*sz)
summaries = append(summaries, &e)
diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go
index f40df828a..4f572fb88 100644
--- a/pkg/domain/infra/abi/network.go
+++ b/pkg/domain/infra/abi/network.go
@@ -43,21 +43,26 @@ func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.Net
return reports, nil
}
-func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.NetworkInspectOptions) ([]entities.NetworkInspectReport, error) {
+func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]entities.NetworkInspectReport, []error, error) {
config, err := ic.Libpod.GetConfig()
if err != nil {
- return nil, err
+ return nil, nil, err
}
-
+ var errs []error
rawCNINetworks := make([]entities.NetworkInspectReport, 0, len(namesOrIds))
for _, name := range namesOrIds {
rawList, err := network.InspectNetwork(config, name)
if err != nil {
- return nil, err
+ if errors.Cause(err) == define.ErrNoSuchNetwork {
+ errs = append(errs, errors.Errorf("no such network %s", name))
+ continue
+ } else {
+ return nil, nil, errors.Wrapf(err, "error inspecting network %s", name)
+ }
}
rawCNINetworks = append(rawCNINetworks, rawList)
}
- return rawCNINetworks, nil
+ return rawCNINetworks, errs, nil
}
func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) {
diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go
index a7c66bae6..348570a20 100644
--- a/pkg/domain/infra/abi/play.go
+++ b/pkg/domain/infra/abi/play.go
@@ -341,7 +341,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
}
named, err := reference.ParseNormalizedNamed(container.Image)
if err != nil {
- return nil, err
+ return nil, errors.Wrapf(err, "Failed to parse image %q", container.Image)
}
// In kube, if the image is tagged with latest, it should always pull
if tagged, isTagged := named.(reference.NamedTagged); isTagged {
diff --git a/pkg/domain/infra/abi/terminal/sigproxy_linux.go b/pkg/domain/infra/abi/terminal/sigproxy_linux.go
index f484e926c..0c586cf5c 100644
--- a/pkg/domain/infra/abi/terminal/sigproxy_linux.go
+++ b/pkg/domain/infra/abi/terminal/sigproxy_linux.go
@@ -5,12 +5,17 @@ import (
"syscall"
"github.com/containers/podman/v2/libpod"
+ "github.com/containers/podman/v2/libpod/shutdown"
"github.com/containers/podman/v2/pkg/signal"
"github.com/sirupsen/logrus"
)
// ProxySignals ...
func ProxySignals(ctr *libpod.Container) {
+ // Stop catching the shutdown signals (SIGINT, SIGTERM) - they're going
+ // to the container now.
+ shutdown.Stop()
+
sigBuffer := make(chan os.Signal, 128)
signal.CatchAll(sigBuffer)
diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go
index 946f258af..a7262f61b 100644
--- a/pkg/domain/infra/abi/volumes.go
+++ b/pkg/domain/infra/abi/volumes.go
@@ -4,6 +4,7 @@ import (
"context"
"github.com/containers/podman/v2/libpod"
+ "github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/domain/filters"
"github.com/containers/podman/v2/pkg/domain/infra/abi/parse"
@@ -71,9 +72,10 @@ func (ic *ContainerEngine) VolumeRm(ctx context.Context, namesOrIds []string, op
return reports, nil
}
-func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []string, opts entities.VolumeInspectOptions) ([]*entities.VolumeInspectReport, error) {
+func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []string, opts entities.InspectOptions) ([]*entities.VolumeInspectReport, []error, error) {
var (
err error
+ errs []error
vols []*libpod.Volume
)
@@ -82,13 +84,18 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
if opts.All {
vols, err = ic.Libpod.GetAllVolumes()
if err != nil {
- return nil, err
+ return nil, nil, err
}
} else {
for _, v := range namesOrIds {
vol, err := ic.Libpod.LookupVolume(v)
if err != nil {
- return nil, errors.Wrapf(err, "error inspecting volume %s", v)
+ if errors.Cause(err) == define.ErrNoSuchVolume {
+ errs = append(errs, errors.Errorf("no such volume %s", v))
+ continue
+ } else {
+ return nil, nil, errors.Wrapf(err, "error inspecting volume %s", v)
+ }
}
vols = append(vols, vol)
}
@@ -98,11 +105,11 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
var uid, gid int
uid, err = v.UID()
if err != nil {
- return nil, err
+ return nil, nil, err
}
gid, err = v.GID()
if err != nil {
- return nil, err
+ return nil, nil, err
}
config := entities.VolumeConfigResponse{
Name: v.Name(),
@@ -117,7 +124,7 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
}
reports = append(reports, &entities.VolumeInspectReport{VolumeConfigResponse: &config})
}
- return reports, nil
+ return reports, errs, nil
}
func (ic *ContainerEngine) VolumePrune(ctx context.Context) ([]*entities.VolumePruneReport, error) {
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index 1bb4e68ac..8066e1c00 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -21,6 +21,7 @@ import (
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/errorhandling"
"github.com/containers/podman/v2/pkg/specgen"
+ "github.com/containers/podman/v2/pkg/util"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -29,8 +30,8 @@ func (ic *ContainerEngine) ContainerRunlabel(ctx context.Context, label string,
return errors.New("not implemented")
}
-func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string) (*entities.BoolReport, error) {
- exists, err := containers.Exists(ic.ClientCxt, nameOrID)
+func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string, options entities.ContainerExistsOptions) (*entities.BoolReport, error) {
+ exists, err := containers.Exists(ic.ClientCxt, nameOrID, options.External)
return &entities.BoolReport{Value: exists}, err
}
@@ -500,7 +501,6 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
if err == define.ErrDetach {
// User manually detached
// Exit cleanly immediately
- report.Err = err
reports = append(reports, &report)
return reports, nil
}
@@ -559,6 +559,11 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
for _, w := range con.Warnings {
fmt.Fprintf(os.Stderr, "%s\n", w)
}
+ if opts.CIDFile != "" {
+ if err := util.CreateCidFile(opts.CIDFile, con.ID); err != nil {
+ return nil, err
+ }
+ }
report := entities.ContainerRunReport{Id: con.ID}
@@ -573,6 +578,10 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
// Attach
if err := startAndAttach(ic, con.ID, &opts.DetachKeys, opts.InputStream, opts.OutputStream, opts.ErrorStream); err != nil {
+ if err == define.ErrDetach {
+ return &report, nil
+ }
+
report.ExitCode = define.ExitCode(err)
if opts.Rm {
if rmErr := containers.Remove(ic.ClientCxt, con.ID, bindings.PFalse, bindings.PTrue); rmErr != nil {
diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go
index d155fdd9e..15527e02c 100644
--- a/pkg/domain/infra/tunnel/network.go
+++ b/pkg/domain/infra/tunnel/network.go
@@ -5,22 +5,34 @@ import (
"github.com/containers/podman/v2/pkg/bindings/network"
"github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/pkg/errors"
)
func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]*entities.NetworkListReport, error) {
return network.List(ic.ClientCxt, options)
}
-func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.NetworkInspectOptions) ([]entities.NetworkInspectReport, error) {
- reports := make([]entities.NetworkInspectReport, 0, len(namesOrIds))
+func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]entities.NetworkInspectReport, []error, error) {
+ var (
+ reports = make([]entities.NetworkInspectReport, 0, len(namesOrIds))
+ errs = []error{}
+ )
for _, name := range namesOrIds {
report, err := network.Inspect(ic.ClientCxt, name)
if err != nil {
- return nil, err
+ errModel, ok := err.(entities.ErrorModel)
+ if !ok {
+ return nil, nil, err
+ }
+ if errModel.ResponseCode == 404 {
+ errs = append(errs, errors.Errorf("no such network %q", name))
+ continue
+ }
+ return nil, nil, err
}
reports = append(reports, report...)
}
- return reports, nil
+ return reports, errs, nil
}
func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) {
diff --git a/pkg/domain/infra/tunnel/volumes.go b/pkg/domain/infra/tunnel/volumes.go
index e432d3292..c0df2bb7b 100644
--- a/pkg/domain/infra/tunnel/volumes.go
+++ b/pkg/domain/infra/tunnel/volumes.go
@@ -5,6 +5,7 @@ import (
"github.com/containers/podman/v2/pkg/bindings/volumes"
"github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/pkg/errors"
)
func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.VolumeCreateOptions) (*entities.IDOrNameResponse, error) {
@@ -35,25 +36,36 @@ func (ic *ContainerEngine) VolumeRm(ctx context.Context, namesOrIds []string, op
return reports, nil
}
-func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []string, opts entities.VolumeInspectOptions) ([]*entities.VolumeInspectReport, error) {
+func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []string, opts entities.InspectOptions) ([]*entities.VolumeInspectReport, []error, error) {
+ var (
+ reports = make([]*entities.VolumeInspectReport, 0, len(namesOrIds))
+ errs = []error{}
+ )
if opts.All {
vols, err := volumes.List(ic.ClientCxt, nil)
if err != nil {
- return nil, err
+ return nil, nil, err
}
for _, v := range vols {
namesOrIds = append(namesOrIds, v.Name)
}
}
- reports := make([]*entities.VolumeInspectReport, 0, len(namesOrIds))
for _, id := range namesOrIds {
data, err := volumes.Inspect(ic.ClientCxt, id)
if err != nil {
- return nil, err
+ errModel, ok := err.(entities.ErrorModel)
+ if !ok {
+ return nil, nil, err
+ }
+ if errModel.ResponseCode == 404 {
+ errs = append(errs, errors.Errorf("no such volume %q", id))
+ continue
+ }
+ return nil, nil, err
}
reports = append(reports, &entities.VolumeInspectReport{VolumeConfigResponse: data})
}
- return reports, nil
+ return reports, errs, nil
}
func (ic *ContainerEngine) VolumePrune(ctx context.Context) ([]*entities.VolumePruneReport, error) {
diff --git a/pkg/spec/security.go b/pkg/spec/security.go
index e152e3495..5f7db7edb 100644
--- a/pkg/spec/security.go
+++ b/pkg/spec/security.go
@@ -178,7 +178,7 @@ func (c *SecurityConfig) ConfigureGenerator(g *generate.Generator, user *UserCon
for _, opt := range c.SecurityOpts {
// Split on both : and =
- splitOpt := strings.Split(opt, "=")
+ splitOpt := strings.SplitN(opt, "=", 2)
if len(splitOpt) == 1 {
splitOpt = strings.Split(opt, ":")
}
diff --git a/pkg/spec/storage.go b/pkg/spec/storage.go
index 5e2f04e50..ebf5ec196 100644
--- a/pkg/spec/storage.go
+++ b/pkg/spec/storage.go
@@ -394,7 +394,7 @@ func getBindMount(args []string) (spec.Mount, error) {
var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel bool
for _, val := range args {
- kv := strings.Split(val, "=")
+ kv := strings.SplitN(val, "=", 2)
switch kv[0] {
case "bind-nonrecursive":
newMount.Options = append(newMount.Options, "bind")
@@ -517,7 +517,7 @@ func getTmpfsMount(args []string) (spec.Mount, error) {
var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup bool
for _, val := range args {
- kv := strings.Split(val, "=")
+ kv := strings.SplitN(val, "=", 2)
switch kv[0] {
case "tmpcopyup", "notmpcopyup":
if setTmpcopyup {
@@ -591,7 +591,7 @@ func getNamedVolume(args []string) (*libpod.ContainerNamedVolume, error) {
var setSource, setDest, setRORW, setSuid, setDev, setExec bool
for _, val := range args {
- kv := strings.Split(val, "=")
+ kv := strings.SplitN(val, "=", 2)
switch kv[0] {
case "ro", "rw":
if setRORW {
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index 105e36bc6..f051537de 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -4,6 +4,7 @@ import (
"context"
"os"
"path/filepath"
+ "strings"
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v2/libpod"
@@ -91,11 +92,19 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
if err != nil {
return nil, err
}
- imgName := s.Image
- names := newImage.Names()
- if len(names) > 0 {
- imgName = names[0]
+ // If the input name changed, we could properly resolve the
+ // image. Otherwise, it must have been an ID where we're
+ // defaulting to the first name or an empty one if no names are
+ // present.
+ imgName := newImage.InputName
+ if s.Image == newImage.InputName && strings.HasPrefix(newImage.ID(), s.Image) {
+ imgName = ""
+ names := newImage.Names()
+ if len(names) > 0 {
+ imgName = names[0]
+ }
}
+
options = append(options, libpod.WithRootFSFromImage(newImage.ID(), imgName, s.RawImageName))
}
if err := s.Validate(); err != nil {
diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go
index 7adb8be6a..7e4f09dc4 100644
--- a/pkg/specgen/generate/namespaces.go
+++ b/pkg/specgen/generate/namespaces.go
@@ -342,7 +342,7 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt
return errors.Wrapf(err, "error looking up container to share uts namespace with")
}
hostname = utsCtr.Hostname()
- case s.NetNS.NSMode == specgen.Host || s.UtsNS.NSMode == specgen.Host:
+ case (s.NetNS.NSMode == specgen.Host && hostname == "") || s.UtsNS.NSMode == specgen.Host:
tmpHostname, err := os.Hostname()
if err != nil {
return errors.Wrap(err, "unable to retrieve hostname of the host")
diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go
index f02432f5b..8454458a8 100644
--- a/pkg/specgen/generate/oci.go
+++ b/pkg/specgen/generate/oci.go
@@ -110,7 +110,7 @@ func makeCommand(ctx context.Context, s *specgen.SpecGenerator, img *image.Image
// Only use image command if the user did not manually set an
// entrypoint.
command := s.Command
- if command == nil && img != nil && s.Entrypoint == nil {
+ if (command == nil || len(command) == 0) && img != nil && (s.Entrypoint == nil || len(s.Entrypoint) == 0) {
newCmd, err := img.Cmd(ctx)
if err != nil {
return nil, err
diff --git a/pkg/specgen/generate/ports.go b/pkg/specgen/generate/ports.go
index 7dd50ac0d..5c13c95b2 100644
--- a/pkg/specgen/generate/ports.go
+++ b/pkg/specgen/generate/ports.go
@@ -25,7 +25,12 @@ const (
func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping, map[string]map[string]map[uint16]uint16, map[string]map[string]map[uint16]uint16, error) {
// First, we need to validate the ports passed in the specgen, and then
// convert them into CNI port mappings.
- finalMappings := []ocicni.PortMapping{}
+ type tempMapping struct {
+ mapping ocicni.PortMapping
+ startOfRange bool
+ isInRange bool
+ }
+ tempMappings := []tempMapping{}
// To validate, we need two maps: one for host ports, one for container
// ports.
@@ -153,18 +158,32 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping,
Protocol: p,
HostIP: port.HostIP,
}
- finalMappings = append(finalMappings, cniPort)
+ tempMappings = append(
+ tempMappings,
+ tempMapping{
+ mapping: cniPort,
+ startOfRange: port.Range > 0 && index == 0,
+ isInRange: port.Range > 0,
+ },
+ )
}
}
}
// Handle any 0 host ports now by setting random container ports.
if postAssignHostPort {
- remadeMappings := make([]ocicni.PortMapping, 0, len(finalMappings))
+ remadeMappings := make([]ocicni.PortMapping, 0, len(tempMappings))
+
+ var (
+ candidate int
+ err error
+ )
// Iterate over all
- for _, p := range finalMappings {
- if p.HostPort != 0 {
+ for _, tmp := range tempMappings {
+ p := tmp.mapping
+
+ if p.HostPort != 0 && !tmp.isInRange {
remadeMappings = append(remadeMappings, p)
continue
}
@@ -192,9 +211,15 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping,
// Max retries to ensure we don't loop forever.
for i := 0; i < 15; i++ {
- candidate, err := getRandomPort()
- if err != nil {
- return nil, nil, nil, errors.Wrapf(err, "error getting candidate host port for container port %d", p.ContainerPort)
+ // Only get a random candidate for single entries or the start
+ // of a range. Otherwise we just increment the candidate.
+ if !tmp.isInRange || tmp.startOfRange {
+ candidate, err = getRandomPort()
+ if err != nil {
+ return nil, nil, nil, errors.Wrapf(err, "error getting candidate host port for container port %d", p.ContainerPort)
+ }
+ } else {
+ candidate++
}
if hostPortMap[uint16(candidate)] == 0 {
@@ -213,6 +238,11 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping,
return remadeMappings, containerPortValidate, hostPortValidate, nil
}
+ finalMappings := []ocicni.PortMapping{}
+ for _, m := range tempMappings {
+ finalMappings = append(finalMappings, m.mapping)
+ }
+
return finalMappings, containerPortValidate, hostPortValidate, nil
}
diff --git a/pkg/terminal/util.go b/pkg/terminal/util.go
index ab3dc54e4..169bec2af 100644
--- a/pkg/terminal/util.go
+++ b/pkg/terminal/util.go
@@ -12,6 +12,7 @@ import (
"github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh"
+ "golang.org/x/crypto/ssh/knownhosts"
"golang.org/x/crypto/ssh/terminal"
"k8s.io/client-go/util/homedir"
)
@@ -114,6 +115,9 @@ func HostKey(host string) ssh.PublicKey {
return nil
}
+ // support -H parameter for ssh-keyscan
+ hashhost := knownhosts.HashHostname(host)
+
scanner := bufio.NewScanner(fd)
for scanner.Scan() {
_, hosts, key, _, _, err := ssh.ParseKnownHosts(scanner.Bytes())
@@ -123,7 +127,7 @@ func HostKey(host string) ssh.PublicKey {
}
for _, h := range hosts {
- if h == host {
+ if h == host || h == hashhost {
return key
}
}
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index c3a70e2fb..a9aad657d 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -474,8 +474,8 @@ func getTomlStorage(storeOptions *storage.StoreOptions) *tomlConfig {
config.Storage.RunRoot = storeOptions.RunRoot
config.Storage.GraphRoot = storeOptions.GraphRoot
for _, i := range storeOptions.GraphDriverOptions {
- s := strings.Split(i, "=")
- if s[0] == "overlay.mount_program" {
+ s := strings.SplitN(i, "=", 2)
+ if s[0] == "overlay.mount_program" && len(s) == 2 {
config.Storage.Options.MountProgram = s[1]
}
}
@@ -638,3 +638,18 @@ func ValidateSysctls(strSlice []string) (map[string]string, error) {
func DefaultContainerConfig() *config.Config {
return containerConfig
}
+
+func CreateCidFile(cidfile string, id string) error {
+ cidFile, err := OpenExclusiveFile(cidfile)
+ if err != nil {
+ if os.IsExist(err) {
+ return errors.Errorf("container id file exists. Ensure another container is not using it or delete %s", cidfile)
+ }
+ return errors.Errorf("error opening cidfile %s", cidfile)
+ }
+ if _, err = cidFile.WriteString(id); err != nil {
+ logrus.Error(err)
+ }
+ cidFile.Close()
+ return nil
+}
diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go
index ef310d590..af6c43fec 100644
--- a/pkg/varlinkapi/images.go
+++ b/pkg/varlinkapi/images.go
@@ -843,7 +843,7 @@ func (i *VarlinkAPI) ImageSave(call iopodman.VarlinkCall, options iopodman.Image
saveOutput := bytes.NewBuffer([]byte{})
c := make(chan error)
go func() {
- err := newImage.Save(getContext(), options.Name, options.Format, output, options.MoreTags, options.Quiet, options.Compress)
+ err := newImage.Save(getContext(), options.Name, options.Format, output, options.MoreTags, options.Quiet, options.Compress, true)
c <- err
close(c)
}()