diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/api/handlers/compat/containers_create.go | 2 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/containers.go | 30 | ||||
-rw-r--r-- | pkg/api/server/server.go | 24 | ||||
-rw-r--r-- | pkg/bindings/containers/attach.go | 52 | ||||
-rw-r--r-- | pkg/bindings/containers/containers.go | 6 | ||||
-rw-r--r-- | pkg/bindings/test/containers_test.go | 12 | ||||
-rw-r--r-- | pkg/domain/entities/containers.go | 5 | ||||
-rw-r--r-- | pkg/domain/entities/engine_container.go | 2 | ||||
-rw-r--r-- | pkg/domain/infra/abi/containers.go | 16 | ||||
-rw-r--r-- | pkg/domain/infra/abi/containers_runlabel.go | 3 | ||||
-rw-r--r-- | pkg/domain/infra/abi/play.go | 2 | ||||
-rw-r--r-- | pkg/domain/infra/abi/terminal/sigproxy_linux.go | 5 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/containers.go | 9 | ||||
-rw-r--r-- | pkg/specgen/generate/ports.go | 46 |
14 files changed, 164 insertions, 50 deletions
diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go index 4ce31cc83..8a0b3c922 100644 --- a/pkg/api/handlers/compat/containers_create.go +++ b/pkg/api/handlers/compat/containers_create.go @@ -238,7 +238,7 @@ func makeCreateConfig(ctx context.Context, containerConfig *config.Config, input Pod: "", // podman PodmanPath: "", // podman Quiet: false, // front-end only - Resources: createconfig.CreateResourceConfig{}, + Resources: createconfig.CreateResourceConfig{MemorySwappiness: -1}, RestartPolicy: input.HostConfig.RestartPolicy.Name, Rm: input.HostConfig.AutoRemove, StopSignal: stopSignal, 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/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/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..46b169284 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 { diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 803a59932..a20d3b404 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) diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 614fd5fe0..60dbbce6c 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -74,11 +74,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 } 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/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/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 1bb4e68ac..7913d79cd 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -29,8 +29,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 +500,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 } @@ -573,6 +572,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/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 } |