summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/api/handlers/compat/containers_create.go15
-rw-r--r--pkg/api/handlers/compat/networks.go6
-rw-r--r--pkg/bindings/generator/generator.go234
-rw-r--r--pkg/bindings/images/removeoptions_types.go93
-rw-r--r--pkg/bindings/images/rm.go10
-rw-r--r--pkg/bindings/images/types.go8
-rw-r--r--pkg/bindings/test/images_test.go17
-rw-r--r--pkg/bindings/test/manifests_test.go2
-rw-r--r--pkg/domain/entities/container_ps.go2
-rw-r--r--pkg/domain/entities/containers.go6
-rw-r--r--pkg/domain/infra/abi/containers.go4
-rw-r--r--pkg/domain/infra/tunnel/containers.go53
-rw-r--r--pkg/ps/ps.go37
13 files changed, 442 insertions, 45 deletions
diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go
index 409a74de2..6e85872b2 100644
--- a/pkg/api/handlers/compat/containers_create.go
+++ b/pkg/api/handlers/compat/containers_create.go
@@ -66,7 +66,20 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "make cli opts()"))
return
}
- sg := specgen.NewSpecGenerator(newImage.ID(), cliOpts.RootFS)
+
+ imgNameOrID := newImage.ID()
+ // if the img had multi names with the same sha256 ID, should use the InputName, not the ID
+ if len(newImage.Names()) > 1 {
+ imageRef, err := utils.ParseDockerReference(newImage.InputName)
+ if err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err)
+ return
+ }
+ // maybe the InputName has no tag, so use full name to display
+ imgNameOrID = imageRef.DockerReference().String()
+ }
+
+ sg := specgen.NewSpecGenerator(imgNameOrID, cliOpts.RootFS)
if err := common.FillOutSpecGen(sg, cliOpts, args); err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "fill out specgen"))
return
diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go
index fe13971b0..f0b922885 100644
--- a/pkg/api/handlers/compat/networks.go
+++ b/pkg/api/handlers/compat/networks.go
@@ -131,7 +131,7 @@ func getNetworkResourceByNameOrID(nameOrID string, runtime *libpod.Runtime, filt
Name: conf.Name,
ID: network.GetNetworkID(conf.Name),
Created: time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)), // nolint: unconvert
- Scope: "",
+ Scope: "local",
Driver: network.DefaultNetworkDriver,
EnableIPv6: false,
IPAM: dockerNetwork.IPAM{
@@ -197,7 +197,7 @@ func ListNetworks(w http.ResponseWriter, r *http.Request) {
}
var reports []*types.NetworkResource
- logrus.Errorf("netNames: %q", strings.Join(netNames, ", "))
+ logrus.Debugf("netNames: %q", strings.Join(netNames, ", "))
for _, name := range netNames {
report, err := getNetworkResourceByNameOrID(name, runtime, query.Filters)
if err != nil {
@@ -239,7 +239,7 @@ func CreateNetwork(w http.ResponseWriter, r *http.Request) {
Internal: networkCreate.Internal,
Labels: networkCreate.Labels,
}
- if networkCreate.IPAM != nil && networkCreate.IPAM.Config != nil {
+ if networkCreate.IPAM != nil && len(networkCreate.IPAM.Config) > 0 {
if len(networkCreate.IPAM.Config) > 1 {
utils.InternalServerError(w, errors.New("compat network create can only support one IPAM config"))
return
diff --git a/pkg/bindings/generator/generator.go b/pkg/bindings/generator/generator.go
new file mode 100644
index 000000000..24c2310ff
--- /dev/null
+++ b/pkg/bindings/generator/generator.go
@@ -0,0 +1,234 @@
+package main
+
+import (
+ "errors"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "strings"
+ "text/template"
+ "time"
+)
+
+var bodyTmpl = `package {{.PackageName}}
+
+import (
+{{range $import := .Imports}} {{$import}}
+{{end}}
+
+)
+
+/*
+This file is generated automatically by go generate. Do not edit.
+
+Created {{.Date}}
+*/
+
+// Changed
+func (o *{{.StructName}}) Changed(fieldName string) bool {
+ r := reflect.ValueOf(o)
+ value := reflect.Indirect(r).FieldByName(fieldName)
+ return !value.IsNil()
+}
+
+// ToParams
+func (o *{{.StructName}}) ToParams() (url.Values, error) {
+ params := url.Values{}
+ if o == nil {
+ return params, nil
+ }
+ json := jsoniter.ConfigCompatibleWithStandardLibrary
+ s := reflect.ValueOf(o)
+ if reflect.Ptr == s.Kind() {
+ s = s.Elem()
+ }
+ sType := s.Type()
+ for i := 0; i < s.NumField(); i++ {
+ fieldName := sType.Field(i).Name
+ if !o.Changed(fieldName) {
+ continue
+ }
+ f := s.Field(i)
+ if reflect.Ptr == f.Kind() {
+ f = f.Elem()
+ }
+ switch f.Kind() {
+ case reflect.Bool:
+ params.Set(fieldName, strconv.FormatBool(f.Bool()))
+ case reflect.String:
+ params.Set(fieldName, f.String())
+ case reflect.Int, reflect.Int64:
+ // f.Int() is always an int64
+ params.Set(fieldName, strconv.FormatInt(f.Int(), 10))
+ case reflect.Slice:
+ typ := reflect.TypeOf(f.Interface()).Elem()
+ slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap())
+ switch typ.Kind() {
+ case reflect.String:
+ s, ok := slice.Interface().([]string)
+ if !ok {
+ return nil, errors.New("failed to convert to string slice")
+ }
+ for _, val := range s {
+ params.Add(fieldName, val)
+ }
+ default:
+ return nil, errors.Errorf("unknown slice type %s", f.Kind().String())
+ }
+ case reflect.Map:
+ lowerCaseKeys := make(map[string][]string)
+ // I dont know if this code is needed anymore, TBD
+ // for k, v := range filters {
+ // lowerCaseKeys[strings.ToLower(k)] = v
+ // }
+ s, err := json.MarshalToString(lowerCaseKeys)
+ if err != nil {
+ return nil, err
+ }
+
+ params.Set(fieldName, s)
+ default:
+ return nil, errors.Errorf("unknown type %s", f.Kind().String())
+ }
+ }
+ return params, nil
+}
+`
+
+var fieldTmpl = `
+// With{{.Name}}
+func(o *{{.StructName}}) With{{.Name}}(value {{.Type}}) *{{.StructName}} {
+ v := &value
+ o.{{.Name}} = v
+ return o
+}
+`
+
+type fieldStruct struct {
+ Name string
+ StructName string
+ Type string
+}
+
+func main() {
+ var (
+ closed bool
+ fieldStructs []fieldStruct
+ structNode ast.Node
+ )
+ srcFile := os.Getenv("GOFILE")
+ pkg := os.Getenv("GOPACKAGE")
+ inputStructName := os.Args[1]
+ b, err := ioutil.ReadFile(srcFile)
+ if err != nil {
+ panic(err)
+ }
+ fset := token.NewFileSet() // positions are relative to fset
+ f, err := parser.ParseFile(fset, "", b, parser.ParseComments)
+ if err != nil {
+ panic(err)
+ }
+
+ // always add reflect
+ imports := []string{"\"reflect\""}
+ for _, imp := range f.Imports {
+ imports = append(imports, imp.Path.Value)
+ }
+
+ out, err := os.Create(strings.ToLower(inputStructName) + "_" + srcFile)
+ if err != nil {
+ panic(err)
+ }
+ defer func() {
+ if !closed {
+ out.Close()
+ }
+ }()
+ bodyStruct := struct {
+ PackageName string
+ Imports []string
+ Date string
+ StructName string
+ }{
+ PackageName: pkg,
+ Imports: imports,
+ Date: time.Now().String(),
+ StructName: inputStructName,
+ }
+
+ body := template.Must(template.New("body").Parse(bodyTmpl))
+ fields := template.Must(template.New("fields").Parse(fieldTmpl))
+ ast.Inspect(f, func(n ast.Node) bool {
+ ref, refOK := n.(*ast.TypeSpec)
+ if refOK {
+ if ref.Name.Name == inputStructName {
+ structNode = n
+ x := ref.Type.(*ast.StructType)
+ for _, field := range x.Fields.List {
+ var (
+ name string
+ )
+ typeExpr := field.Type
+ start := typeExpr.Pos() - 1
+ end := typeExpr.End() - 1
+ fieldType := strings.Replace(string(b[start:end]), "*", "", 1)
+ if len(field.Names) > 0 {
+ name = field.Names[0].Name
+ if len(name) < 1 {
+ panic(errors.New("bad name"))
+ }
+ }
+ fStruct := fieldStruct{
+ Name: name,
+ StructName: inputStructName,
+ Type: fieldType,
+ }
+ fieldStructs = append(fieldStructs, fStruct)
+ } // for
+
+ // create the body
+ if err := body.Execute(out, bodyStruct); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+
+ // create with func from the struct fields
+ for _, fs := range fieldStructs {
+ if err := fields.Execute(out, fs); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+ }
+
+ // close out file
+ if err := out.Close(); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+ closed = true
+
+ // go fmt file
+ gofmt := exec.Command("gofmt", "-w", "-s", out.Name())
+ gofmt.Stderr = os.Stdout
+ if err := gofmt.Run(); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+
+ // go import file
+ goimport := exec.Command("goimports", "-w", out.Name())
+ goimport.Stderr = os.Stdout
+ if err := goimport.Run(); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+ }
+
+ }
+ return true
+ })
+}
diff --git a/pkg/bindings/images/removeoptions_types.go b/pkg/bindings/images/removeoptions_types.go
new file mode 100644
index 000000000..5902bf908
--- /dev/null
+++ b/pkg/bindings/images/removeoptions_types.go
@@ -0,0 +1,93 @@
+package images
+
+import (
+ "net/url"
+ "reflect"
+ "strconv"
+
+ jsoniter "github.com/json-iterator/go"
+ "github.com/pkg/errors"
+)
+
+/*
+This file is generated automatically by go generate. Do not edit.
+
+Created 2020-12-10 12:51:06.090426622 -0600 CST m=+0.000133169
+*/
+
+// Changed
+func (o *RemoveOptions) Changed(fieldName string) bool {
+ r := reflect.ValueOf(o)
+ value := reflect.Indirect(r).FieldByName(fieldName)
+ return !value.IsNil()
+}
+
+// ToParams
+func (o *RemoveOptions) ToParams() (url.Values, error) {
+ params := url.Values{}
+ if o == nil {
+ return params, nil
+ }
+ json := jsoniter.ConfigCompatibleWithStandardLibrary
+ s := reflect.ValueOf(o)
+ if reflect.Ptr == s.Kind() {
+ s = s.Elem()
+ }
+ sType := s.Type()
+ for i := 0; i < s.NumField(); i++ {
+ fieldName := sType.Field(i).Name
+ if !o.Changed(fieldName) {
+ continue
+ }
+ f := s.Field(i)
+ if reflect.Ptr == f.Kind() {
+ f = f.Elem()
+ }
+ switch f.Kind() {
+ case reflect.Bool:
+ params.Set(fieldName, strconv.FormatBool(f.Bool()))
+ case reflect.String:
+ params.Set(fieldName, f.String())
+ case reflect.Int, reflect.Int64:
+ // f.Int() is always an int64
+ params.Set(fieldName, strconv.FormatInt(f.Int(), 10))
+ case reflect.Slice:
+ typ := reflect.TypeOf(f.Interface()).Elem()
+ slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap())
+ switch typ.Kind() {
+ case reflect.String:
+ s, ok := slice.Interface().([]string)
+ if !ok {
+ return nil, errors.New("failed to convert to string slice")
+ }
+ for _, val := range s {
+ params.Add(fieldName, val)
+ }
+ default:
+ return nil, errors.Errorf("unknown slice type %s", f.Kind().String())
+ }
+ case reflect.Map:
+ lowerCaseKeys := make(map[string][]string)
+ // I dont know if this code is needed anymore, TBD
+ // for k, v := range filters {
+ // lowerCaseKeys[strings.ToLower(k)] = v
+ // }
+ s, err := json.MarshalToString(lowerCaseKeys)
+ if err != nil {
+ return nil, err
+ }
+
+ params.Set(fieldName, s)
+ default:
+ return nil, errors.Errorf("unknown type %s", f.Kind().String())
+ }
+ }
+ return params, nil
+}
+
+// WithForce
+func (o *RemoveOptions) WithForce(value bool) *RemoveOptions {
+ v := &value
+ o.Force = v
+ return o
+}
diff --git a/pkg/bindings/images/rm.go b/pkg/bindings/images/rm.go
index 9685b75e4..0b3b88165 100644
--- a/pkg/bindings/images/rm.go
+++ b/pkg/bindings/images/rm.go
@@ -41,17 +41,19 @@ func BatchRemove(ctx context.Context, images []string, opts entities.ImageRemove
return &report.ImageRemoveReport, errorhandling.StringsToErrors(report.Errors)
}
-// Remove removes an image from the local storage. Use force to remove an
+// Remove removes an image from the local storage. Use optional force option to remove an
// image, even if it's used by containers.
-func Remove(ctx context.Context, nameOrID string, force bool) (*entities.ImageRemoveReport, error) {
+func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) (*entities.ImageRemoveReport, error) {
var report handlers.LibpodImagesRemoveReport
conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
- params := url.Values{}
- params.Set("force", strconv.FormatBool(force))
+ params, err := options.ToParams()
+ if err != nil {
+ return nil, err
+ }
response, err := conn.DoRequest(nil, http.MethodDelete, "/images/%s", params, nil, nameOrID)
if err != nil {
return nil, err
diff --git a/pkg/bindings/images/types.go b/pkg/bindings/images/types.go
new file mode 100644
index 000000000..340c7bdb9
--- /dev/null
+++ b/pkg/bindings/images/types.go
@@ -0,0 +1,8 @@
+package images
+
+//go:generate go run ../generator/generator.go RemoveOptions
+// RemoveOptions are optional options for image removal
+type RemoveOptions struct {
+ // Forces removes all containers based on the image
+ Force *bool
+}
diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go
index 7d9415f91..684f110e8 100644
--- a/pkg/bindings/test/images_test.go
+++ b/pkg/bindings/test/images_test.go
@@ -84,7 +84,7 @@ var _ = Describe("Podman images", func() {
// Test to validate the remove image api
It("remove image", func() {
// Remove invalid image should be a 404
- response, err := images.Remove(bt.conn, "foobar5000", false)
+ response, err := images.Remove(bt.conn, "foobar5000", nil)
Expect(err).ToNot(BeNil())
Expect(response).To(BeNil())
code, _ := bindings.CheckResponseCode(err)
@@ -93,7 +93,7 @@ var _ = Describe("Podman images", func() {
// Remove an image by name, validate image is removed and error is nil
inspectData, err := images.GetImage(bt.conn, busybox.shortName, nil)
Expect(err).To(BeNil())
- response, err = images.Remove(bt.conn, busybox.shortName, false)
+ response, err = images.Remove(bt.conn, busybox.shortName, nil)
Expect(err).To(BeNil())
code, _ = bindings.CheckResponseCode(err)
@@ -113,12 +113,13 @@ var _ = Describe("Podman images", func() {
// try to remove the image "alpine". This should fail since we are not force
// deleting hence image cannot be deleted until the container is deleted.
- response, err = images.Remove(bt.conn, alpine.shortName, false)
+ response, err = images.Remove(bt.conn, alpine.shortName, nil)
code, _ = bindings.CheckResponseCode(err)
Expect(code).To(BeNumerically("==", http.StatusConflict))
// Removing the image "alpine" where force = true
- response, err = images.Remove(bt.conn, alpine.shortName, true)
+ options := images.RemoveOptions{}
+ response, err = images.Remove(bt.conn, alpine.shortName, options.WithForce(true))
Expect(err).To(BeNil())
// To be extra sure, check if the previously created container
// is gone as well.
@@ -213,7 +214,7 @@ var _ = Describe("Podman images", func() {
It("Load|Import Image", func() {
// load an image
- _, err := images.Remove(bt.conn, alpine.name, false)
+ _, err := images.Remove(bt.conn, alpine.name, nil)
Expect(err).To(BeNil())
exists, err := images.Exists(bt.conn, alpine.name)
Expect(err).To(BeNil())
@@ -231,7 +232,7 @@ var _ = Describe("Podman images", func() {
// load with a repo name
f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName))
Expect(err).To(BeNil())
- _, err = images.Remove(bt.conn, alpine.name, false)
+ _, err = images.Remove(bt.conn, alpine.name, nil)
Expect(err).To(BeNil())
exists, err = images.Exists(bt.conn, alpine.name)
Expect(err).To(BeNil())
@@ -247,7 +248,7 @@ var _ = Describe("Podman images", func() {
// load with a bad repo name should trigger a 500
f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName))
Expect(err).To(BeNil())
- _, err = images.Remove(bt.conn, alpine.name, false)
+ _, err = images.Remove(bt.conn, alpine.name, nil)
Expect(err).To(BeNil())
exists, err = images.Exists(bt.conn, alpine.name)
Expect(err).To(BeNil())
@@ -275,7 +276,7 @@ var _ = Describe("Podman images", func() {
It("Import Image", func() {
// load an image
- _, err = images.Remove(bt.conn, alpine.name, false)
+ _, err = images.Remove(bt.conn, alpine.name, nil)
Expect(err).To(BeNil())
exists, err := images.Exists(bt.conn, alpine.name)
Expect(err).To(BeNil())
diff --git a/pkg/bindings/test/manifests_test.go b/pkg/bindings/test/manifests_test.go
index 55fc4cb0d..a4ecaa20f 100644
--- a/pkg/bindings/test/manifests_test.go
+++ b/pkg/bindings/test/manifests_test.go
@@ -47,7 +47,7 @@ var _ = Describe("Podman containers ", func() {
code, _ := bindings.CheckResponseCode(err)
Expect(code).To(BeNumerically("==", http.StatusInternalServerError))
- _, err = images.Remove(bt.conn, id, false)
+ _, err = images.Remove(bt.conn, id, nil)
Expect(err).To(BeNil())
// create manifest list with images
diff --git a/pkg/domain/entities/container_ps.go b/pkg/domain/entities/container_ps.go
index b4e8446cb..ff3b087ed 100644
--- a/pkg/domain/entities/container_ps.go
+++ b/pkg/domain/entities/container_ps.go
@@ -12,6 +12,8 @@ import (
// Listcontainer describes a container suitable for listing
type ListContainer struct {
+ // AutoRemove
+ AutoRemove bool
// Container command
Command []string
// Container creation time
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
index 39d679eaf..01086a2b3 100644
--- a/pkg/domain/entities/containers.go
+++ b/pkg/domain/entities/containers.go
@@ -227,8 +227,10 @@ type ContainerLogsOptions struct {
Tail int64
// Show timestamps in the logs.
Timestamps bool
- // Write the logs to Writer.
- Writer io.Writer
+ // Write the stdout to this Writer.
+ StdoutWriter io.Writer
+ // Write the stderr to this Writer.
+ StderrWriter io.Writer
}
// ExecOptions describes the cli values to exec into
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index ff4277a2e..ec65dbe44 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -925,7 +925,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
}
func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []string, options entities.ContainerLogsOptions) error {
- if options.Writer == nil {
+ if options.StdoutWriter == nil && options.StderrWriter == nil {
return errors.New("no io.Writer set for container logs")
}
@@ -963,7 +963,7 @@ func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []strin
}()
for line := range logChannel {
- fmt.Fprintln(options.Writer, line.String(logOpts))
+ line.Write(options.StdoutWriter, options.StderrWriter, logOpts)
}
return nil
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index e65fef0a4..ad7688f62 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -360,11 +360,12 @@ func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecG
func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIDs []string, options entities.ContainerLogsOptions) error {
since := options.Since.Format(time.RFC3339)
tail := strconv.FormatInt(options.Tail, 10)
- stdout := options.Writer != nil
+ stdout := options.StdoutWriter != nil
+ stderr := options.StderrWriter != nil
opts := containers.LogOptions{
Follow: &options.Follow,
Since: &since,
- Stderr: &stdout,
+ Stderr: &stderr,
Stdout: &stdout,
Tail: &tail,
Timestamps: &options.Timestamps,
@@ -372,10 +373,11 @@ func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIDs []string,
}
var err error
- outCh := make(chan string)
+ stdoutCh := make(chan string)
+ stderrCh := make(chan string)
ctx, cancel := context.WithCancel(context.Background())
go func() {
- err = containers.Logs(ic.ClientCxt, nameOrIDs[0], opts, outCh, outCh)
+ err = containers.Logs(ic.ClientCxt, nameOrIDs[0], opts, stdoutCh, stderrCh)
cancel()
}()
@@ -383,8 +385,14 @@ func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIDs []string,
select {
case <-ctx.Done():
return err
- case line := <-outCh:
- _, _ = io.WriteString(options.Writer, line+"\n")
+ case line := <-stdoutCh:
+ if options.StdoutWriter != nil {
+ _, _ = io.WriteString(options.StdoutWriter, line+"\n")
+ }
+ case line := <-stderrCh:
+ if options.StderrWriter != nil {
+ _, _ = io.WriteString(options.StderrWriter, line+"\n")
+ }
}
}
}
@@ -515,6 +523,29 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
reports = append(reports, &report)
return reports, errors.Wrapf(report.Err, "unable to start container %s", name)
}
+ if ctr.AutoRemove {
+ // Defer the removal, so we can return early if needed and
+ // de-spaghetti the code.
+ defer func() {
+ shouldRestart, err := containers.ShouldRestart(ic.ClientCxt, ctr.ID)
+ if err != nil {
+ logrus.Errorf("Failed to check if %s should restart: %v", ctr.ID, err)
+ return
+ }
+
+ if !shouldRestart {
+ if err := containers.Remove(ic.ClientCxt, ctr.ID, bindings.PFalse, bindings.PTrue); err != nil {
+ if errorhandling.Contains(err, define.ErrNoSuchCtr) ||
+ errorhandling.Contains(err, define.ErrCtrRemoved) {
+ logrus.Warnf("Container %s does not exist: %v", ctr.ID, err)
+ } else {
+ logrus.Errorf("Error removing container %s: %v", ctr.ID, err)
+ }
+ }
+ }
+ }()
+ }
+
exitCode, err := containers.Wait(ic.ClientCxt, name, nil)
if err == define.ErrNoSuchCtr {
// Check events
@@ -535,6 +566,16 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
if !ctrRunning {
err = containers.Start(ic.ClientCxt, name, &options.DetachKeys)
if err != nil {
+ if ctr.AutoRemove {
+ if err := containers.Remove(ic.ClientCxt, ctr.ID, bindings.PFalse, bindings.PTrue); err != nil {
+ if errorhandling.Contains(err, define.ErrNoSuchCtr) ||
+ errorhandling.Contains(err, define.ErrCtrRemoved) {
+ logrus.Warnf("Container %s does not exist: %v", ctr.ID, err)
+ } else {
+ logrus.Errorf("Error removing container %s: %v", ctr.ID, err)
+ }
+ }
+ }
report.Err = errors.Wrapf(err, "unable to start container %q", name)
report.ExitCode = define.ExitCode(err)
reports = append(reports, &report)
diff --git a/pkg/ps/ps.go b/pkg/ps/ps.go
index cfdf3ee49..6c26e8708 100644
--- a/pkg/ps/ps.go
+++ b/pkg/ps/ps.go
@@ -179,24 +179,25 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities
}
ps := entities.ListContainer{
- Command: conConfig.Command,
- Created: conConfig.CreatedTime,
- Exited: exited,
- ExitCode: exitCode,
- ExitedAt: exitedTime.Unix(),
- ID: conConfig.ID,
- Image: conConfig.RootfsImageName,
- ImageID: conConfig.RootfsImageID,
- IsInfra: conConfig.IsInfra,
- Labels: conConfig.Labels,
- Mounts: ctr.UserVolumes(),
- Names: []string{conConfig.Name},
- Pid: pid,
- Pod: conConfig.Pod,
- Ports: portMappings,
- Size: size,
- StartedAt: startedTime.Unix(),
- State: conState.String(),
+ AutoRemove: ctr.AutoRemove(),
+ Command: conConfig.Command,
+ Created: conConfig.CreatedTime,
+ Exited: exited,
+ ExitCode: exitCode,
+ ExitedAt: exitedTime.Unix(),
+ ID: conConfig.ID,
+ Image: conConfig.RootfsImageName,
+ ImageID: conConfig.RootfsImageID,
+ IsInfra: conConfig.IsInfra,
+ Labels: conConfig.Labels,
+ Mounts: ctr.UserVolumes(),
+ Names: []string{conConfig.Name},
+ Pid: pid,
+ Pod: conConfig.Pod,
+ Ports: portMappings,
+ Size: size,
+ StartedAt: startedTime.Unix(),
+ State: conState.String(),
}
if opts.Pod && len(conConfig.Pod) > 0 {
podName, err := rt.GetName(conConfig.Pod)