diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/api/handlers/compat/containers.go | 13 | ||||
-rw-r--r-- | pkg/api/handlers/compat/images_build.go | 2 | ||||
-rw-r--r-- | pkg/api/handlers/swagger/swagger.go | 7 | ||||
-rw-r--r-- | pkg/api/handlers/types.go | 11 | ||||
-rw-r--r-- | pkg/api/server/register_containers.go | 17 | ||||
-rw-r--r-- | pkg/bindings/containers/containers.go | 11 | ||||
-rw-r--r-- | pkg/bindings/containers/types.go | 1 | ||||
-rw-r--r-- | pkg/bindings/containers/types_remove_options.go | 15 | ||||
-rw-r--r-- | pkg/bindings/test/containers_test.go | 47 | ||||
-rw-r--r-- | pkg/domain/entities/containers.go | 6 | ||||
-rw-r--r-- | pkg/domain/entities/engine.go | 1 | ||||
-rw-r--r-- | pkg/domain/entities/engine_container.go | 2 | ||||
-rw-r--r-- | pkg/domain/entities/reports/containers.go | 28 | ||||
-rw-r--r-- | pkg/domain/infra/abi/containers.go | 37 | ||||
-rw-r--r-- | pkg/domain/infra/abi/images.go | 50 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/containers.go | 90 | ||||
-rw-r--r-- | pkg/machine/config.go | 21 | ||||
-rw-r--r-- | pkg/machine/qemu/machine.go | 3 | ||||
-rw-r--r-- | pkg/specgen/generate/ports.go | 2 | ||||
-rw-r--r-- | pkg/specgen/podspecgen.go | 2 | ||||
-rw-r--r-- | pkg/specgen/specgen.go | 2 |
21 files changed, 242 insertions, 126 deletions
diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index ad341c3ab..4539199d3 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -36,6 +36,7 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) { query := struct { Force bool `schema:"force"` Ignore bool `schema:"ignore"` + Depend bool `schema:"depend"` Link bool `schema:"link"` Timeout *uint `schema:"timeout"` DockerVolumes bool `schema:"v"` @@ -57,6 +58,7 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) { if utils.IsLibpodRequest(r) { options.Volumes = query.LibpodVolumes options.Timeout = query.Timeout + options.Depend = query.Depend } else { if query.Link { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, @@ -71,7 +73,7 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) { // code. containerEngine := abi.ContainerEngine{Libpod: runtime} name := utils.GetName(r) - report, err := containerEngine.ContainerRm(r.Context(), []string{name}, options) + reports, err := containerEngine.ContainerRm(r.Context(), []string{name}, options) if err != nil { if errors.Cause(err) == define.ErrNoSuchCtr { utils.ContainerNotFound(w, name, err) @@ -81,8 +83,8 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } - if len(report) > 0 && report[0].Err != nil { - err = report[0].Err + if len(reports) > 0 && reports[0].Err != nil { + err = reports[0].Err if errors.Cause(err) == define.ErrNoSuchCtr { utils.ContainerNotFound(w, name, err) return @@ -90,7 +92,10 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } - + if utils.IsLibpodRequest(r) { + utils.WriteResponse(w, http.StatusOK, reports) + return + } utils.WriteResponse(w, http.StatusNoContent, nil) } diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index 0fcac5330..2d296b5ce 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -138,7 +138,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { // if layers field not set assume its not from a valid podman-client // could be a docker client, set `layers=true` since that is the default - // expected behviour + // expected behaviour if !utils.IsLibpodRequest(r) { if _, found := r.URL.Query()["layers"]; !found { query.Layers = true diff --git a/pkg/api/handlers/swagger/swagger.go b/pkg/api/handlers/swagger/swagger.go index 9844839b7..7868ff206 100644 --- a/pkg/api/handlers/swagger/swagger.go +++ b/pkg/api/handlers/swagger/swagger.go @@ -111,6 +111,13 @@ type swagLibpodInspectImageResponse struct { } } +// Rm containers +// swagger:response DocsLibpodContainerRmReport +type swagLibpodContainerRmReport struct { + // in: body + Body []handlers.LibpodContainersRmReport +} + // Prune containers // swagger:response DocsContainerPruneReport type swagContainerPruneReport struct { diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go index f850db3d8..588758b2c 100644 --- a/pkg/api/handlers/types.go +++ b/pkg/api/handlers/types.go @@ -53,6 +53,17 @@ type LibpodContainersPruneReport struct { PruneError string `json:"Err,omitempty"` } +type LibpodContainersRmReport struct { + ID string `json:"Id"` + // Error which occurred during Rm operation (if any). + // This field is optional and may be omitted if no error occurred. + // + // Extensions: + // x-omitempty: true + // x-nullable: true + RmError string `json:"Err,omitempty"` +} + type Info struct { docker.Info BuildahVersion string diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index 601e1251b..4d19c04d4 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -817,9 +817,22 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // required: true // description: the name or ID of the container // - in: query + // name: depend + // type: boolean + // description: additionally remove containers that depend on the container to be removed + // - in: query // name: force // type: boolean - // description: need something + // description: force stop container if running + // - in: query + // name: ignore + // type: boolean + // description: ignore errors when the container to be removed does not existxo + // - in: query + // name: timeout + // type: integer + // default: 10 + // description: number of seconds to wait before killing container when force removing // - in: query // name: v // type: boolean @@ -827,6 +840,8 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // produces: // - application/json // responses: + // 200: + // $ref: "#/responses/DocsLibpodContainerRmReport" // 204: // description: no error // 400: diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go index 14a173025..0148e62cb 100644 --- a/pkg/bindings/containers/containers.go +++ b/pkg/bindings/containers/containers.go @@ -78,25 +78,26 @@ func Prune(ctx context.Context, options *PruneOptions) ([]*reports.PruneReport, // The volumes bool dictates that a container's volumes should also be removed. // The All option indicates that all containers should be removed // The Ignore option indicates that if a container did not exist, ignore the error -func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) error { +func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) ([]*reports.RmReport, error) { if options == nil { options = new(RemoveOptions) } + var reports []*reports.RmReport conn, err := bindings.GetClient(ctx) if err != nil { - return err + return reports, err } params, err := options.ToParams() if err != nil { - return err + return reports, err } response, err := conn.DoRequest(ctx, nil, http.MethodDelete, "/containers/%s", params, nil, nameOrID) if err != nil { - return err + return reports, err } defer response.Body.Close() - return response.Process(nil) + return reports, response.Process(&reports) } // Inspect returns low level information about a Container. The nameOrID can be a container name diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go index 81a53a549..db3eb3e1b 100644 --- a/pkg/bindings/containers/types.go +++ b/pkg/bindings/containers/types.go @@ -138,6 +138,7 @@ type PruneOptions struct { //go:generate go run ../generator/generator.go RemoveOptions // RemoveOptions are optional options for removing containers type RemoveOptions struct { + Depend *bool Ignore *bool Force *bool Volumes *bool diff --git a/pkg/bindings/containers/types_remove_options.go b/pkg/bindings/containers/types_remove_options.go index 1e52e819d..7fa198d2f 100644 --- a/pkg/bindings/containers/types_remove_options.go +++ b/pkg/bindings/containers/types_remove_options.go @@ -17,6 +17,21 @@ func (o *RemoveOptions) ToParams() (url.Values, error) { return util.ToParams(o) } +// WithDepend set field Depend to given value +func (o *RemoveOptions) WithDepend(value bool) *RemoveOptions { + o.Depend = &value + return o +} + +// GetDepend returns value of field Depend +func (o *RemoveOptions) GetDepend() bool { + if o.Depend == nil { + var z bool + return z + } + return *o.Depend +} + // WithIgnore set field Ignore to given value func (o *RemoveOptions) WithIgnore(value bool) *RemoveOptions { o.Ignore = &value diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go index b6c06756b..cab032a40 100644 --- a/pkg/bindings/test/containers_test.go +++ b/pkg/bindings/test/containers_test.go @@ -175,7 +175,7 @@ var _ = Describe("Podman containers ", func() { Expect(err).To(BeNil()) err = containers.Pause(bt.conn, cid, nil) Expect(err).To(BeNil()) - err = containers.Remove(bt.conn, cid, nil) + _, err = containers.Remove(bt.conn, cid, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -188,8 +188,10 @@ var _ = Describe("Podman containers ", func() { Expect(err).To(BeNil()) err = containers.Pause(bt.conn, cid, nil) Expect(err).To(BeNil()) - err = containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithForce(true)) + rmResponse, err := containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithForce(true)) Expect(err).To(BeNil()) + Expect(len(reports.RmReportsErrs(rmResponse))).To(Equal(0)) + Expect(len(reports.RmReportsIds(rmResponse))).To(Equal(1)) }) It("podman stop a paused container by name", func() { @@ -669,7 +671,8 @@ var _ = Describe("Podman containers ", func() { }) It("podman remove bogus container", func() { - err = containers.Remove(bt.conn, "foobar", nil) + _, err := containers.Remove(bt.conn, "foobar", nil) + Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) }) @@ -679,7 +682,7 @@ var _ = Describe("Podman containers ", func() { _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, name, nil) + _, err = containers.Remove(bt.conn, name, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -690,7 +693,7 @@ var _ = Describe("Podman containers ", func() { cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, cid, nil) + _, err = containers.Remove(bt.conn, cid, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -700,22 +703,22 @@ var _ = Describe("Podman containers ", func() { var name = "top" _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) - // Removing running container should fail - err = containers.Remove(bt.conn, name, new(containers.RemoveOptions).WithForce(true)) + // Removing running container should succeed + rmResponse, err := containers.Remove(bt.conn, name, new(containers.RemoveOptions).WithForce(true)) Expect(err).To(BeNil()) - //code, _ := bindings.CheckResponseCode(err) - //Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) + Expect(len(reports.RmReportsErrs(rmResponse))).To(Equal(0)) + Expect(len(reports.RmReportsIds(rmResponse))).To(Equal(1)) }) It("podman forcibly remove running container by ID", func() { var name = "top" cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) - // Removing running container should fail - err = containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithForce(true)) + // Forcably Removing running container should succeed + rmResponse, err := containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithForce(true)) Expect(err).To(BeNil()) - //code, _ := bindings.CheckResponseCode(err) - //Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) + Expect(len(reports.RmReportsErrs(rmResponse))).To(Equal(0)) + Expect(len(reports.RmReportsIds(rmResponse))).To(Equal(1)) }) It("podman remove running container and volume by name", func() { @@ -723,7 +726,7 @@ var _ = Describe("Podman containers ", func() { _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, name, new(containers.RemoveOptions).WithVolumes(true)) + _, err = containers.Remove(bt.conn, name, new(containers.RemoveOptions).WithVolumes(true)) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -734,7 +737,7 @@ var _ = Describe("Podman containers ", func() { cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithVolumes(true)) + _, err = containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithVolumes(true)) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -744,11 +747,11 @@ var _ = Describe("Podman containers ", func() { var name = "top" _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) - // Removing running container should fail - err = containers.Remove(bt.conn, name, new(containers.RemoveOptions).WithVolumes(true).WithForce(true)) + // Forcibly Removing running container should succeed + rmResponse, err := containers.Remove(bt.conn, name, new(containers.RemoveOptions).WithVolumes(true).WithForce(true)) Expect(err).To(BeNil()) - //code, _ := bindings.CheckResponseCode(err) - //Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) + Expect(len(reports.RmReportsErrs(rmResponse))).To(Equal(0)) + Expect(len(reports.RmReportsIds(rmResponse))).To(Equal(1)) }) It("podman forcibly remove running container and volume by ID", func() { @@ -756,10 +759,10 @@ var _ = Describe("Podman containers ", func() { cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithForce(true).WithVolumes(true)) + rmResponse, err := containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithForce(true).WithVolumes(true)) Expect(err).To(BeNil()) - //code, _ := bindings.CheckResponseCode(err) - //Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) + Expect(len(reports.RmReportsErrs(rmResponse))).To(Equal(0)) + Expect(len(reports.RmReportsIds(rmResponse))).To(Equal(1)) }) It("List containers with filters", func() { diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index ae441b7f3..e3f8f1b7c 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -129,6 +129,7 @@ type RestartReport struct { type RmOptions struct { All bool + Depend bool Force bool Ignore bool Latest bool @@ -136,11 +137,6 @@ type RmOptions struct { Volumes bool } -type RmReport struct { - Err error - Id string //nolint -} - type ContainerInspectReport struct { *define.InspectContainerData } diff --git a/pkg/domain/entities/engine.go b/pkg/domain/entities/engine.go index a8023f7cf..055af7ff9 100644 --- a/pkg/domain/entities/engine.go +++ b/pkg/domain/entities/engine.go @@ -40,6 +40,7 @@ type PodmanConfig struct { Identity string // ssh identity for connecting to server MaxWorks int // maximum number of parallel threads MemoryProfile string // Hidden: Should memory profile be taken + NoOut bool // Don't output to stdout RegistriesConf string // allows for specifying a custom registries.conf Remote bool // Connection to Podman API Service will use RESTful API RuntimePath string // --runtime flag will set Engine.RuntimePath diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 383e42098..7ce4dd0f6 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -40,7 +40,7 @@ type ContainerEngine interface { ContainerRename(ctr context.Context, nameOrID string, options ContainerRenameOptions) error ContainerRestart(ctx context.Context, namesOrIds []string, options RestartOptions) ([]*RestartReport, error) ContainerRestore(ctx context.Context, namesOrIds []string, options RestoreOptions) ([]*RestoreReport, error) - ContainerRm(ctx context.Context, namesOrIds []string, options RmOptions) ([]*RmReport, error) + ContainerRm(ctx context.Context, namesOrIds []string, options RmOptions) ([]*reports.RmReport, error) ContainerRun(ctx context.Context, opts ContainerRunOptions) (*ContainerRunReport, error) ContainerRunlabel(ctx context.Context, label string, image string, args []string, opts ContainerRunlabelOptions) error ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error) diff --git a/pkg/domain/entities/reports/containers.go b/pkg/domain/entities/reports/containers.go new file mode 100644 index 000000000..54bcd092b --- /dev/null +++ b/pkg/domain/entities/reports/containers.go @@ -0,0 +1,28 @@ +package reports + +type RmReport struct { + Id string `json:"Id"` //nolint + Err error `json:"Err,omitempty"` +} + +func RmReportsIds(r []*RmReport) []string { + ids := make([]string, 0, len(r)) + for _, v := range r { + if v == nil || v.Id == "" { + continue + } + ids = append(ids, v.Id) + } + return ids +} + +func RmReportsErrs(r []*RmReport) []error { + errs := make([]error, 0, len(r)) + for _, v := range r { + if v == nil || v.Err == nil { + continue + } + errs = append(errs, v.Err) + } + return errs +} diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index bf4dcff62..a4522698e 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -301,27 +301,27 @@ func (ic *ContainerEngine) removeContainer(ctx context.Context, ctr *libpod.Cont return err } -func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, options entities.RmOptions) ([]*entities.RmReport, error) { - reports := []*entities.RmReport{} +func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, options entities.RmOptions) ([]*reports.RmReport, error) { + rmReports := []*reports.RmReport{} names := namesOrIds // Attempt to remove named containers directly from storage, if container is defined in libpod // this will fail and code will fall through to removing the container from libpod.` tmpNames := []string{} for _, ctr := range names { - report := entities.RmReport{Id: ctr} + report := reports.RmReport{Id: ctr} report.Err = ic.Libpod.RemoveStorageContainer(ctr, options.Force) switch errors.Cause(report.Err) { case nil: // remove container names that we successfully deleted - reports = append(reports, &report) + rmReports = append(rmReports, &report) case define.ErrNoSuchCtr, define.ErrCtrExists: // There is still a potential this is a libpod container tmpNames = append(tmpNames, ctr) default: if _, err := ic.Libpod.LookupContainer(ctr); errors.Cause(err) == define.ErrNoSuchCtr { // remove container failed, but not a libpod container - reports = append(reports, &report) + rmReports = append(rmReports, &report) continue } // attempt to remove as a libpod container @@ -340,23 +340,34 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, for _, ctr := range names { logrus.Debugf("Evicting container %q", ctr) - report := entities.RmReport{Id: ctr} + report := reports.RmReport{Id: ctr} _, err := ic.Libpod.EvictContainer(ctx, ctr, options.Volumes) if err != nil { if options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr { logrus.Debugf("Ignoring error (--allow-missing): %v", err) - reports = append(reports, &report) + rmReports = append(rmReports, &report) continue } report.Err = err - reports = append(reports, &report) + rmReports = append(rmReports, &report) continue } - reports = append(reports, &report) + rmReports = append(rmReports, &report) } - return reports, nil + return rmReports, nil } + if !options.All && options.Depend { + // Add additional containers based on dependencies to container map + for _, ctr := range ctrs { + reports, err := ic.Libpod.RemoveDepend(ctx, ctr, options.Force, options.Volumes, options.Timeout) + if err != nil { + return rmReports, err + } + rmReports = append(rmReports, reports...) + } + return rmReports, nil + } errMap, err := parallelctr.ContainerOp(ctx, ctrs, func(c *libpod.Container) error { return ic.removeContainer(ctx, c, options) }) @@ -364,12 +375,12 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, return nil, err } for ctr, err := range errMap { - report := new(entities.RmReport) + report := new(reports.RmReport) report.Id = ctr.ID() report.Err = err - reports = append(reports, report) + rmReports = append(rmReports, report) } - return reports, nil + return rmReports, nil } func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, []error, error) { diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 84c83ea8e..592e0f4e3 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -745,11 +745,17 @@ func putSignature(manifestBlob []byte, mech signature.SigningMechanism, sigStore // TransferRootless creates new podman processes using exec.Command and sudo, transferring images between the given source and destination users func transferRootless(source entities.ImageScpOptions, dest entities.ImageScpOptions, podman string, parentFlags []string) error { var cmdSave *exec.Cmd - saveCommand := parentFlags - saveCommand = append(saveCommand, []string{"save", "--output", source.File, source.Image}...) + saveCommand, loadCommand := parentFlags, parentFlags + saveCommand = append(saveCommand, []string{"save"}...) + loadCommand = append(loadCommand, []string{"load"}...) + if source.Quiet { + saveCommand = append(saveCommand, "-q") + loadCommand = append(loadCommand, "-q") + } + + saveCommand = append(saveCommand, []string{"--output", source.File, source.Image}...) - loadCommand := parentFlags - loadCommand = append(loadCommand, []string{"load", "--input", dest.File}...) + loadCommand = append(loadCommand, []string{"--input", dest.File}...) if source.User == "root" { cmdSave = exec.Command("sudo", podman) @@ -757,7 +763,7 @@ func transferRootless(source entities.ImageScpOptions, dest entities.ImageScpOpt cmdSave = exec.Command(podman) } cmdSave = utils.CreateSCPCommand(cmdSave, saveCommand) - logrus.Debug("Executing save command") + logrus.Debugf("Executing save command: %q", cmdSave) err := cmdSave.Run() if err != nil { return err @@ -770,20 +776,22 @@ func transferRootless(source entities.ImageScpOptions, dest entities.ImageScpOpt cmdLoad = exec.Command(podman) } cmdLoad = utils.CreateSCPCommand(cmdLoad, loadCommand) - logrus.Debug("Executing load command") - err = cmdLoad.Run() - if err != nil { - return err - } - return nil + logrus.Debugf("Executing load command: %q", cmdLoad) + return cmdLoad.Run() } -// TransferRootless creates new podman processes using exec.Command and su/machinectl, transferring images between the given source and destination users +// TransferRootful creates new podman processes using exec.Command and su/machinectl, transferring images between the given source and destination users func transferRootful(source entities.ImageScpOptions, dest entities.ImageScpOptions, podman string, parentFlags []string) error { basicCommand := []string{podman} basicCommand = append(basicCommand, parentFlags...) - saveCommand := append(basicCommand, []string{"save", "--output", source.File, source.Image}...) - loadCommand := append(basicCommand, []string{"load", "--input", dest.File}...) + saveCommand := append(basicCommand, "save") + loadCommand := append(basicCommand, "load") + if source.Quiet { + saveCommand = append(saveCommand, "-q") + loadCommand = append(loadCommand, "-q") + } + saveCommand = append(saveCommand, []string{"--output", source.File, source.Image}...) + loadCommand = append(loadCommand, []string{"--input", dest.File}...) save := []string{strings.Join(saveCommand, " ")} load := []string{strings.Join(loadCommand, " ")} @@ -846,18 +854,18 @@ func lookupUser(u string) (*user.User, error) { func execSu(execUser *user.User, command []string) error { cmd := exec.Command("su", "-l", execUser.Username, "--command") cmd = utils.CreateSCPCommand(cmd, command) - logrus.Debug("Executing command su") + logrus.Debugf("Executing via su: %q", cmd) return cmd.Run() } func execMachine(execUser *user.User, command []string, machinectl string) error { - var cmd *exec.Cmd + verb := machinectl + args := []string{"shell", "-q", execUser.Username + "@.host"} if execUser.Uid == "0" { - cmd = exec.Command("sudo", machinectl, "shell", "-q", execUser.Username+"@.host") - } else { - cmd = exec.Command(machinectl, "shell", "-q", execUser.Username+"@.host") + args = append([]string{verb}, args...) + verb = "sudo" } - cmd = utils.CreateSCPCommand(cmd, command) - logrus.Debug("Executing command machinectl") + cmd := utils.CreateSCPCommand(exec.Command(verb, args...), command) + logrus.Debugf("Executing via machinectl: %q", cmd) return cmd.Run() } diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 2127f8749..4f72eab96 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -182,9 +182,9 @@ func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []st return reports, nil } -func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, opts entities.RmOptions) ([]*entities.RmReport, error) { +func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, opts entities.RmOptions) ([]*reports.RmReport, error) { // TODO there is no endpoint for container eviction. Need to discuss - options := new(containers.RemoveOptions).WithForce(opts.Force).WithVolumes(opts.Volumes).WithIgnore(opts.Ignore) + options := new(containers.RemoveOptions).WithForce(opts.Force).WithVolumes(opts.Volumes).WithIgnore(opts.Ignore).WithDepend(opts.Depend) if opts.Timeout != nil { options = options.WithTimeout(*opts.Timeout) } @@ -193,25 +193,31 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, if err != nil { return nil, err } - reports := make([]*entities.RmReport, 0, len(ctrs)) + rmReports := make([]*reports.RmReport, 0, len(ctrs)) for _, c := range ctrs { - reports = append(reports, &entities.RmReport{ - Id: c.ID, - Err: containers.Remove(ic.ClientCtx, c.ID, options), - }) + report, err := containers.Remove(ic.ClientCtx, c.ID, options) + if err != nil { + return rmReports, err + } + rmReports = append(rmReports, report...) } - return reports, nil + return rmReports, nil } - reports := make([]*entities.RmReport, 0, len(namesOrIds)) + rmReports := make([]*reports.RmReport, 0, len(namesOrIds)) for _, name := range namesOrIds { - reports = append(reports, &entities.RmReport{ - Id: name, - Err: containers.Remove(ic.ClientCtx, name, options), - }) + report, err := containers.Remove(ic.ClientCtx, name, options) + if err != nil { + rmReports = append(rmReports, &reports.RmReport{ + Id: name, + Err: err, + }) + continue + } + rmReports = append(rmReports, report...) } - return reports, nil + return rmReports, nil } func (ic *ContainerEngine) ContainerPrune(ctx context.Context, opts entities.ContainerPruneOptions) ([]*reports.PruneReport, error) { @@ -552,6 +558,27 @@ func startAndAttach(ic *ContainerEngine, name string, detachKeys *string, input, return <-attachErr } +func logIfRmError(id string, err error, reports []*reports.RmReport) { + logError := func(id string, err error) { + if errorhandling.Contains(err, define.ErrNoSuchCtr) || + errorhandling.Contains(err, define.ErrCtrRemoved) || + errorhandling.Contains(err, types.ErrLayerUnknown) { + logrus.Debugf("Container %s does not exist: %v", id, err) + } else { + logrus.Errorf("Removing container %s: %v", id, err) + } + } + if err != nil { + logError(id, err) + } else { + for _, report := range reports { + if report.Err != nil { + logError(report.Id, report.Err) + } + } + } +} + func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) { reports := []*entities.ContainerStartReport{} var exitCode = define.ExecErrorCodeGeneric @@ -590,14 +617,8 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri } removeOptions := new(containers.RemoveOptions).WithVolumes(true).WithForce(false) removeContainer := func(id string) { - if err := containers.Remove(ic.ClientCtx, id, removeOptions); err != nil { - if errorhandling.Contains(err, define.ErrNoSuchCtr) || - errorhandling.Contains(err, define.ErrCtrRemoved) { - logrus.Debugf("Container %s does not exist: %v", id, err) - } else { - logrus.Errorf("Removing container %s: %v", id, err) - } - } + reports, err := containers.Remove(ic.ClientCtx, id, removeOptions) + logIfRmError(id, err, reports) } // There can only be one container if attach was used @@ -674,15 +695,8 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri if err != nil { if ctr.AutoRemove { rmOptions := new(containers.RemoveOptions).WithForce(false).WithVolumes(true) - if err := containers.Remove(ic.ClientCtx, ctr.ID, rmOptions); err != nil { - if errorhandling.Contains(err, define.ErrNoSuchCtr) || - errorhandling.Contains(err, define.ErrCtrRemoved) || - errorhandling.Contains(err, types.ErrLayerUnknown) { - logrus.Debugf("Container %s does not exist: %v", ctr.ID, err) - } else { - logrus.Errorf("Removing container %s: %v", ctr.ID, err) - } - } + reports, err := containers.Remove(ic.ClientCtx, ctr.ID, rmOptions) + logIfRmError(ctr.ID, err, reports) } report.Err = errors.Wrapf(err, "unable to start container %q", name) report.ExitCode = define.ExitCode(err) @@ -741,7 +755,8 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta report.ExitCode = define.ExitCode(err) if opts.Rm { - if rmErr := containers.Remove(ic.ClientCtx, con.ID, new(containers.RemoveOptions).WithForce(false).WithVolumes(true)); rmErr != nil { + reports, rmErr := containers.Remove(ic.ClientCtx, con.ID, new(containers.RemoveOptions).WithForce(false).WithVolumes(true)) + if rmErr != nil || reports[0].Err != nil { logrus.Debugf("unable to remove container %s after failing to start and attach to it", con.ID) } } @@ -759,15 +774,8 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta } if !shouldRestart { - if err := containers.Remove(ic.ClientCtx, con.ID, new(containers.RemoveOptions).WithForce(false).WithVolumes(true)); err != nil { - if errorhandling.Contains(err, define.ErrNoSuchCtr) || - errorhandling.Contains(err, define.ErrCtrRemoved) || - errorhandling.Contains(err, types.ErrLayerUnknown) { - logrus.Debugf("Container %s does not exist: %v", con.ID, err) - } else { - logrus.Errorf("Removing container %s: %v", con.ID, err) - } - } + reports, err := containers.Remove(ic.ClientCtx, con.ID, new(containers.RemoveOptions).WithForce(false).WithVolumes(true)) + logIfRmError(con.ID, err, reports) } }() } diff --git a/pkg/machine/config.go b/pkg/machine/config.go index 252ad9768..97237f5e5 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -75,15 +75,18 @@ type Download struct { type ListOptions struct{} type ListResponse struct { - Name string - CreatedAt time.Time - LastUp time.Time - Running bool - Stream string - VMType string - CPUs uint64 - Memory uint64 - DiskSize uint64 + Name string + CreatedAt time.Time + LastUp time.Time + Running bool + Stream string + VMType string + CPUs uint64 + Memory uint64 + DiskSize uint64 + Port int + RemoteUsername string + IdentityPath string } type SSHOptions struct { diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 07f40984c..560037542 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -790,6 +790,9 @@ func GetVMInfos() ([]*machine.ListResponse, error) { listEntry.CPUs = vm.CPUs listEntry.Memory = vm.Memory * units.MiB listEntry.DiskSize = vm.DiskSize * units.GiB + listEntry.Port = vm.Port + listEntry.RemoteUsername = vm.RemoteUsername + listEntry.IdentityPath = vm.IdentityPath fi, err := os.Stat(fullPath) if err != nil { return err diff --git a/pkg/specgen/generate/ports.go b/pkg/specgen/generate/ports.go index b60cc1e98..34b43a62e 100644 --- a/pkg/specgen/generate/ports.go +++ b/pkg/specgen/generate/ports.go @@ -206,7 +206,7 @@ func ParsePortMapping(portMappings []types.PortMapping, exposePorts map[uint16][ } // we do no longer need the original port mappings - // set it to 0 length so we can resuse it to populate + // set it to 0 length so we can reuse it to populate // the slice again while keeping the underlying capacity portMappings = portMappings[:0] diff --git a/pkg/specgen/podspecgen.go b/pkg/specgen/podspecgen.go index 33e8422fd..fdaa714da 100644 --- a/pkg/specgen/podspecgen.go +++ b/pkg/specgen/podspecgen.go @@ -95,7 +95,7 @@ type PodNetworkConfig struct { // Map of networks names ot ids the container should join to. // You can request additional settings for each network, you can // set network aliases, static ips, static mac address and the - // network interface name for this container on the specifc network. + // network interface name for this container on the specific network. // If the map is empty and the bridge network mode is set the container // will be joined to the default network. Networks map[string]types.PerNetworkOptions diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index 5989456c9..6c1011a78 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -426,7 +426,7 @@ type ContainerNetworkConfig struct { // Map of networks names ot ids the container should join to. // You can request additional settings for each network, you can // set network aliases, static ips, static mac address and the - // network interface name for this container on the specifc network. + // network interface name for this container on the specific network. // If the map is empty and the bridge network mode is set the container // will be joined to the default network. Networks map[string]nettypes.PerNetworkOptions |