mirror of
https://github.com/containers/podman.git
synced 2025-05-21 00:56:36 +08:00
Add podman rm --depend
This option causes Podman to not only remove the specified containers but all of the containers that depend on the specified containers. Fixes: https://github.com/containers/podman/issues/10360 Also ran codespell on the code Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
@ -61,7 +61,8 @@ func rmFlags(cmd *cobra.Command) {
|
||||
|
||||
flags.BoolVarP(&rmOptions.All, "all", "a", false, "Remove all containers")
|
||||
flags.BoolVarP(&rmOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified container is missing")
|
||||
flags.BoolVarP(&rmOptions.Force, "force", "f", false, "Force removal of a running or unusable container. The default is false")
|
||||
flags.BoolVarP(&rmOptions.Force, "force", "f", false, "Force removal of a running or unusable container")
|
||||
flags.BoolVar(&rmOptions.Depend, "depend", false, "Remove container and all containers that depend on the selected container")
|
||||
timeFlagName := "time"
|
||||
flags.UintVarP(&stopTimeout, timeFlagName, "t", containerConfig.Engine.StopTimeout, "Seconds to wait for stop before killing the container")
|
||||
_ = cmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone)
|
||||
|
@ -137,7 +137,7 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error {
|
||||
runtimeFlag := cmd.Root().Flags().Lookup("runtime")
|
||||
if runtimeFlag == nil {
|
||||
return errors.Errorf(
|
||||
"Unexcpected error setting runtime to '%s' for restore",
|
||||
"setting runtime to '%s' for restore",
|
||||
*runtime,
|
||||
)
|
||||
}
|
||||
@ -217,7 +217,7 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error {
|
||||
|
||||
context := cmd.Root().LocalFlags().Lookup("context")
|
||||
if context.Value.String() != "default" {
|
||||
return errors.New("Podman does not support swarm, the only --context value allowed is \"default\"")
|
||||
return errors.New("podman does not support swarm, the only --context value allowed is \"default\"")
|
||||
}
|
||||
if !registry.IsRemote() {
|
||||
if cmd.Flag("cpu-profile").Changed {
|
||||
|
@ -27,7 +27,7 @@ func (o OutputErrors) PrintErrors() (lastError error) {
|
||||
instead returns a message and we cast it to a new error.
|
||||
|
||||
Following function performs parsing on build error and returns
|
||||
exit status which was exepected for this current build
|
||||
exit status which was expected for this current build
|
||||
*/
|
||||
func ExitCodeFromBuildError(errorMsg string) (int, error) {
|
||||
if strings.Contains(errorMsg, "exit status") {
|
||||
|
@ -114,7 +114,7 @@ func addPathToRegistry(dir string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Removes all occurences of a directory path from the Windows path stored in the registry
|
||||
// Removes all occurrences of a directory path from the Windows path stored in the registry
|
||||
func removePathFromRegistry(path string) error {
|
||||
k, err := registry.OpenKey(registry.CURRENT_USER, Environment, registry.READ|registry.WRITE)
|
||||
if err != nil {
|
||||
@ -155,7 +155,7 @@ func removePathFromRegistry(path string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Sends a notification message to all top level windows informing them the environmental setings have changed.
|
||||
// Sends a notification message to all top level windows informing them the environmental settings have changed.
|
||||
// Applications such as the Windows command prompt and powershell will know to stop caching stale values on
|
||||
// subsequent restarts. Since applications block the sender when receiving a message, we set a 3 second timeout
|
||||
func broadcastEnvironmentChange() {
|
||||
|
@ -18,6 +18,10 @@ Running or unusable containers will not be removed without the **-f** option.
|
||||
|
||||
Remove all containers. Can be used in conjunction with **-f** as well.
|
||||
|
||||
#### **--depend**
|
||||
|
||||
Remove selected container and recursively remove all containers that depend on it.
|
||||
|
||||
#### **--cidfile**
|
||||
|
||||
Read container ID from the specified file and remove the container. Can be specified multiple times.
|
||||
@ -56,6 +60,11 @@ Remove a container by its name *mywebserver*
|
||||
$ podman rm mywebserver
|
||||
```
|
||||
|
||||
Remove a *mywebserver* container and all of the containers that depend on it
|
||||
```
|
||||
$ podman rm --depend mywebserver
|
||||
```
|
||||
|
||||
Remove several containers by name and container id.
|
||||
```
|
||||
$ podman rm mywebserver myflaskserver 860a4b23
|
||||
|
@ -915,6 +915,37 @@ func (r *Runtime) evictContainer(ctx context.Context, idOrName string, removeVol
|
||||
return id, cleanupErr
|
||||
}
|
||||
|
||||
// RemoveDepend removes all dependencies for a container
|
||||
func (r *Runtime) RemoveDepend(ctx context.Context, rmCtr *Container, force bool, removeVolume bool, timeout *uint) ([]*reports.RmReport, error) {
|
||||
rmReports := make([]*reports.RmReport, 0)
|
||||
deps, err := r.state.ContainerInUse(rmCtr)
|
||||
if err != nil {
|
||||
if err == define.ErrCtrRemoved {
|
||||
return rmReports, nil
|
||||
}
|
||||
return rmReports, err
|
||||
}
|
||||
for _, cid := range deps {
|
||||
ctr, err := r.state.Container(cid)
|
||||
if err != nil {
|
||||
if err == define.ErrNoSuchCtr {
|
||||
continue
|
||||
}
|
||||
return rmReports, err
|
||||
}
|
||||
|
||||
reports, err := r.RemoveDepend(ctx, ctr, force, removeVolume, timeout)
|
||||
if err != nil {
|
||||
return rmReports, err
|
||||
}
|
||||
rmReports = append(rmReports, reports...)
|
||||
}
|
||||
report := reports.RmReport{Id: rmCtr.ID()}
|
||||
report.Err = r.removeContainer(ctx, rmCtr, force, removeVolume, false, timeout)
|
||||
|
||||
return append(rmReports, &report), nil
|
||||
}
|
||||
|
||||
// GetContainer retrieves a container by its ID
|
||||
func (r *Runtime) GetContainer(id string) (*Container, error) {
|
||||
r.lock.RLock()
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
28
pkg/domain/entities/reports/containers.go
Normal file
28
pkg/domain/entities/reports/containers.go
Normal file
@ -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
|
||||
}
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
return reports, nil
|
||||
rmReports = append(rmReports, report...)
|
||||
}
|
||||
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{
|
||||
report, err := containers.Remove(ic.ClientCtx, name, options)
|
||||
if err != nil {
|
||||
rmReports = append(rmReports, &reports.RmReport{
|
||||
Id: name,
|
||||
Err: containers.Remove(ic.ClientCtx, name, options),
|
||||
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)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
@ -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]
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -53,7 +53,7 @@ t GET libpod/images/$IMAGE/json 200 \
|
||||
.RepoTags[-1]=$IMAGE
|
||||
|
||||
# Remove the registry container
|
||||
t DELETE libpod/containers/registry?force=true 204
|
||||
t DELETE libpod/containers/registry?force=true 200
|
||||
|
||||
# Remove images
|
||||
t DELETE libpod/images/$IMAGE 200 \
|
||||
|
@ -85,7 +85,7 @@ else
|
||||
fi
|
||||
fi
|
||||
|
||||
t DELETE libpod/containers/$cid 204
|
||||
t DELETE libpod/containers/$cid 200 .[0].Id=$cid
|
||||
|
||||
# Issue #6799: it should be possible to start a container, even w/o args.
|
||||
t POST libpod/containers/create?name=test_noargs Image=${IMAGE} 201 \
|
||||
@ -100,7 +100,7 @@ t GET libpod/containers/${cid}/json 200 \
|
||||
.State.Status~\\\(exited\\\|stopped\\\) \
|
||||
.State.Running=false \
|
||||
.State.ExitCode=0
|
||||
t DELETE libpod/containers/$cid 204
|
||||
t DELETE libpod/containers/$cid 200 .[0].Id=$cid
|
||||
|
||||
CNAME=myfoo
|
||||
podman run -d --name $CNAME $IMAGE top
|
||||
@ -190,8 +190,8 @@ t GET containers/myctr/json 200 \
|
||||
t DELETE images/localhost/newrepo:latest?force=true 200
|
||||
t DELETE images/localhost/newrepo:v1?force=true 200
|
||||
t DELETE images/localhost/newrepo:v2?force=true 200
|
||||
t DELETE libpod/containers/$cid?force=true 204
|
||||
t DELETE libpod/containers/myctr 204
|
||||
t DELETE libpod/containers/$cid?force=true 200 .[0].Id=$cid
|
||||
t DELETE libpod/containers/myctr 200
|
||||
t DELETE libpod/containers/bogus 404
|
||||
|
||||
|
||||
|
@ -11,7 +11,7 @@ podman run -dt --name mytop $IMAGE top &>/dev/null
|
||||
t GET libpod/containers/mytop/json 200 .State.Status=running
|
||||
t POST libpod/containers/mytop/stop 204
|
||||
t GET libpod/containers/mytop/json 200 .State.Status~\\\(exited\\\|stopped\\\)
|
||||
t DELETE libpod/containers/mytop 204
|
||||
t DELETE libpod/containers/mytop 200
|
||||
|
||||
# stop, by ID
|
||||
# Remember that podman() hides all output; we need to get our CID via inspect
|
||||
@ -21,4 +21,4 @@ t GET libpod/containers/mytop/json 200 .State.Status=running
|
||||
cid=$(jq -r .Id <<<"$output")
|
||||
t POST libpod/containers/$cid/stop 204
|
||||
t GET libpod/containers/mytop/json 200 .State.Status~\\\(exited\\\|stopped\\\)
|
||||
t DELETE libpod/containers/mytop 204
|
||||
t DELETE libpod/containers/mytop 200
|
||||
|
@ -51,7 +51,7 @@ like "$output" ".*merged" "Check container mount"
|
||||
# Unmount the container
|
||||
t POST libpod/containers/foo/unmount 204
|
||||
|
||||
t DELETE libpod/containers/foo?force=true 204
|
||||
t DELETE libpod/containers/foo?force=true 200
|
||||
|
||||
podman run $IMAGE true
|
||||
|
||||
@ -79,7 +79,7 @@ like "$output" ".*metadata:.*" "Check generated kube yaml(service=true) - metada
|
||||
like "$output" ".*spec:.*" "Check generated kube yaml(service=true) - spec"
|
||||
like "$output" ".*kind:\\sService.*" "Check generated kube yaml(service=true) - kind: Service"
|
||||
|
||||
t DELETE libpod/containers/$cid 204
|
||||
t DELETE libpod/containers/$cid 200 .[0].Id=$cid
|
||||
|
||||
# Create 3 stopped containers to test containers prune
|
||||
podman run $IMAGE true
|
||||
|
@ -99,7 +99,7 @@ class ContainerTestCase(APITestCase):
|
||||
|
||||
def test_delete(self):
|
||||
r = requests.delete(self.uri(self.resolve_container("/containers/{}?force=true")))
|
||||
self.assertEqual(r.status_code, 204, r.text)
|
||||
self.assertEqual(r.status_code, 200, r.text)
|
||||
|
||||
def test_stop(self):
|
||||
r = requests.post(self.uri(self.resolve_container("/containers/{}/start")))
|
||||
|
@ -41,7 +41,7 @@ function setup() {
|
||||
# This one must fail
|
||||
run_podman 125 --context=swarm version
|
||||
is "$output" \
|
||||
"Error: Podman does not support swarm, the only --context value allowed is \"default\"" \
|
||||
"Error: podman does not support swarm, the only --context value allowed is \"default\"" \
|
||||
"--context=default or fail"
|
||||
}
|
||||
|
||||
|
@ -58,6 +58,18 @@ load helpers
|
||||
run_podman rm -af
|
||||
}
|
||||
|
||||
@test "podman rm --depend" {
|
||||
run_podman create $IMAGE
|
||||
dependCid=$output
|
||||
run_podman create --net=container:$dependCid $IMAGE
|
||||
cid=$output
|
||||
run_podman 125 rm $dependCid
|
||||
is "$output" "Error: container $dependCid has dependent containers which must be removed before it:.*" "Fail to remove because of dependencies"
|
||||
run_podman rm --depend $dependCid
|
||||
is "$output" ".*$cid" "Container should have been removed"
|
||||
is "$output" ".*$dependCid" "Depend container should have been removed"
|
||||
}
|
||||
|
||||
# I'm sorry! This test takes 13 seconds. There's not much I can do about it,
|
||||
# please know that I think it's justified: podman 1.5.0 had a strange bug
|
||||
# in with exit status was not preserved on some code paths with 'rm -f'
|
||||
|
Reference in New Issue
Block a user