diff options
-rw-r--r-- | .cirrus.yml | 6 | ||||
-rw-r--r-- | CODE-OF-CONDUCT.md | 3 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | cmd/podman/commit.go | 12 | ||||
-rw-r--r-- | cmd/podman/shared/container.go | 5 | ||||
-rw-r--r-- | cmd/podman/shared/create.go | 4 | ||||
-rw-r--r-- | code-of-conduct.md | 55 | ||||
-rw-r--r-- | completions/bash/podman | 12 | ||||
-rw-r--r-- | contrib/spec/podman.spec.in | 8 | ||||
-rw-r--r-- | contrib/spec/python-podman.spec.in | 2 | ||||
-rw-r--r-- | pkg/api/Makefile | 2 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/containers.go | 167 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/swagger.go | 8 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/types.go | 82 | ||||
-rw-r--r-- | pkg/api/handlers/swagger.go | 8 | ||||
-rw-r--r-- | pkg/api/server/register_containers.go | 2 | ||||
-rw-r--r-- | test/e2e/commit_test.go | 19 |
17 files changed, 297 insertions, 100 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index a591c2d38..5c4cf470c 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -337,8 +337,10 @@ meta_task: # Remove old and disused images based on labels set by meta_task image_prune_task: - # Do not run this frequently - only_if: $CIRRUS_BRANCH == $DEST_BRANCH + # This should ONLY ever run from the master branch, and never + # anywhere else so it's behavior is always consistent, even + # as new branches are created. + only_if: $CIRRUS_BRANCH == "master" depends_on: - "meta" diff --git a/CODE-OF-CONDUCT.md b/CODE-OF-CONDUCT.md new file mode 100644 index 000000000..850e68db0 --- /dev/null +++ b/CODE-OF-CONDUCT.md @@ -0,0 +1,3 @@ +## The Libpod Project Community Code of Conduct + +The Libpod project which includes Podman, follows the [Containers Community Code of Conduct](https://github.com/containers/common/blob/master/CODE-OF-CONDUCT.md). @@ -582,7 +582,7 @@ install.libseccomp.sudo: cmd/podman/varlink/iopodman.go: .gopathok cmd/podman/varlink/io.podman.varlink -ifeq ("$(shell uname -o)", "GNU/Linux") +ifneq (,$(findstring Linux,$(shell uname -s))) # Only generate the varlink code on Linux (see issue #4814). GO111MODULE=off $(GO) generate ./cmd/podman/varlink/... endif diff --git a/cmd/podman/commit.go b/cmd/podman/commit.go index 604e8d31c..b4d249c66 100644 --- a/cmd/podman/commit.go +++ b/cmd/podman/commit.go @@ -6,7 +6,6 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/pkg/adapter" - "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -65,17 +64,6 @@ func commitCmd(c *cliconfig.CommitValues) error { if len(args) > 1 { reference = args[1] } - if c.Flag("change").Changed { - for _, change := range c.Change { - splitChange := strings.Split(strings.ToUpper(change), "=") - if len(splitChange) == 1 { - splitChange = strings.Split(strings.ToUpper(change), " ") - } - if !util.StringInSlice(splitChange[0], ChangeCmds) { - return errors.Errorf("invalid syntax for --change: %s", change) - } - } - } iid, err := runtime.Commit(getContext(), c, container, reference) if err != nil { diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go index 9459247ed..ff3846e70 100644 --- a/cmd/podman/shared/container.go +++ b/cmd/podman/shared/container.go @@ -640,6 +640,11 @@ func GetNamespaces(pid int) *Namespace { } } +// GetNamespaceInfo is an exported wrapper for getNamespaceInfo +func GetNamespaceInfo(path string) (string, error) { + return getNamespaceInfo(path) +} + func getNamespaceInfo(path string) (string, error) { val, err := os.Readlink(path) if err != nil { diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go index 010c80373..3062b0ca3 100644 --- a/cmd/podman/shared/create.go +++ b/cmd/podman/shared/create.go @@ -109,11 +109,11 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod. } if overrideOS == "" && imageData.Os != goruntime.GOOS { - return nil, nil, errors.Errorf("incompatible image OS %q on %q host", imageData.Os, goruntime.GOOS) + logrus.Infof("Using %q (OS) image on %q host", imageData.Os, goruntime.GOOS) } if overrideArch == "" && imageData.Architecture != goruntime.GOARCH { - return nil, nil, errors.Errorf("incompatible image architecture %q on %q host", imageData.Architecture, goruntime.GOARCH) + logrus.Infof("Using %q (architecture) on %q host", imageData.Architecture, goruntime.GOARCH) } names := newImage.Names() diff --git a/code-of-conduct.md b/code-of-conduct.md deleted file mode 100644 index 215ce7ac6..000000000 --- a/code-of-conduct.md +++ /dev/null @@ -1,55 +0,0 @@ -## Kubernetes Community Code of Conduct - -### Contributor Code of Conduct - -As contributors and maintainers of this project, and in the interest of fostering -an open and welcoming community, we pledge to respect all people who contribute -through reporting issues, posting feature requests, updating documentation, -submitting pull requests or patches, and other activities. - -We are committed to making participation in this project a harassment-free experience for -everyone, regardless of level of experience, gender, gender identity and expression, -sexual orientation, disability, personal appearance, body size, race, ethnicity, age, -religion, or nationality. - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery. -* Personal attacks. -* Trolling or insulting/derogatory comments. -* Public or private harassment. -* Publishing other's private information, such as physical or electronic addresses, - without explicit permission. -* Other unethical or unprofessional conduct. - -Project maintainers have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are not -aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers -commit themselves to fairly and consistently applying these principles to every aspect -of managing this project. Project maintainers who do not follow or enforce the Code of -Conduct may be permanently removed from the project team. - -This code of conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. - -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting a Kubernetes maintainer, Sarah Novotny <sarahnovotny@google.com>, and/or Dan Kohn <dan@linuxfoundation.org>. - -This Code of Conduct is adapted from the Contributor Covenant -(http://contributor-covenant.org), version 1.2.0, available at -http://contributor-covenant.org/version/1/2/0/ - -### Kubernetes Events Code of Conduct - -Kubernetes events are working conferences intended for professional networking and collaboration in the -Kubernetes community. Attendees are expected to behave according to professional standards and in accordance -with their employer's policies on appropriate workplace behavior. - -While at Kubernetes events or related social networking opportunities, attendees should not engage in -discriminatory or offensive speech or actions regarding gender, sexuality, race, or religion. Speakers should -be especially aware of these concerns. - -The Kubernetes team does not condone any statements by speakers contrary to these standards. The Kubernetes -team reserves the right to deny entrance and/or eject from an event (without refund) any individual found to -be engaging in discriminatory or offensive speech or actions. - -Please bring any concerns to the immediate attention of the Kubernetes event staff. diff --git a/completions/bash/podman b/completions/bash/podman index 56559c142..7c14cf67c 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -1311,7 +1311,6 @@ _podman_build() { --net --network --pid - --runtime --runtime-flag --security-opt --shm-size @@ -1329,15 +1328,10 @@ _podman_build() { -v " - local all_options="$options_with_args $boolean_options" - - case "$prev" in - --runtime) - COMPREPLY=($(compgen -W 'runc runv' -- "$cur")) + case "$prev" in + --file|-f) + COMPREPLY=($(compgen -W "`ls`" -- "$cur")) ;; - $(__podman_to_extglob "$options_with_args")) - return - ;; esac case "$cur" in diff --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in index 4518ecc09..276dd327e 100644 --- a/contrib/spec/podman.spec.in +++ b/contrib/spec/podman.spec.in @@ -508,7 +508,7 @@ export GOPATH=%{buildroot}/%{gopath}:$(pwd)/vendor:%{gopath} %files %license LICENSE -%doc README.md CONTRIBUTING.md pkg/hooks/README-hooks.md install.md code-of-conduct.md transfer.md +%doc README.md CONTRIBUTING.md pkg/hooks/README-hooks.md install.md CODE-OF-CONDUCT.md transfer.md %{_bindir}/%{name} %{_datadir}/bash-completion/completions/* %{_datadir}/zsh/site-functions/* @@ -524,19 +524,19 @@ export GOPATH=%{buildroot}/%{gopath}:$(pwd)/vendor:%{gopath} %if 0%{?with_devel} %files -n libpod-devel -f devel.file-list %license LICENSE -%doc README.md CONTRIBUTING.md pkg/hooks/README-hooks.md install.md code-of-conduct.md transfer.md +%doc README.md CONTRIBUTING.md pkg/hooks/README-hooks.md install.md CODE-OF-CONDUCT.md transfer.md %dir %{gopath}/src/%{provider}.%{provider_tld}/%{project} %endif %if 0%{?with_unit_test} && 0%{?with_devel} %files unit-test-devel -f unit-test-devel.file-list %license LICENSE -%doc README.md CONTRIBUTING.md pkg/hooks/README-hooks.md install.md code-of-conduct.md transfer.md +%doc README.md CONTRIBUTING.md pkg/hooks/README-hooks.md install.md CODE-OF-CONDUCT.md transfer.md %endif %files -n podman-remote %license LICENSE -%doc README.md CONTRIBUTING.md pkg/hooks/README-hooks.md install.md code-of-conduct.md transfer.md +%doc README.md CONTRIBUTING.md pkg/hooks/README-hooks.md install.md CODE-OF-CONDUCT.md transfer.md %{_bindir}/%{name}-remote %if %{with doc} diff --git a/contrib/spec/python-podman.spec.in b/contrib/spec/python-podman.spec.in index 6296586dd..b921f2645 100644 --- a/contrib/spec/python-podman.spec.in +++ b/contrib/spec/python-podman.spec.in @@ -92,7 +92,7 @@ popd %files %license LICENSE -%doc README.md CONTRIBUTING.md install.md code-of-conduct.md transfer.md +%doc README.md CONTRIBUTING.md install.md CODE-OF-CONDUCT.md transfer.md %{_bindir}/pypodman %{_mandir}/man1/pypodman.1* %dir %{python3_sitelib}/podman diff --git a/pkg/api/Makefile b/pkg/api/Makefile index c68e50011..6b24bfd83 100644 --- a/pkg/api/Makefile +++ b/pkg/api/Makefile @@ -9,4 +9,4 @@ validate: ${SWAGGER_OUT} ${SWAGGER_OUT}: # generate doesn't remove file on error rm -f ${SWAGGER_OUT} - swagger generate spec -o ${SWAGGER_OUT} -i tags.yaml -w ./ + swagger generate spec -o ${SWAGGER_OUT} -i tags.yaml -w ./ -m diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go index 54acdbd36..a64ed446c 100644 --- a/pkg/api/handlers/libpod/containers.go +++ b/pkg/api/handlers/libpod/containers.go @@ -1,16 +1,20 @@ package libpod import ( - "fmt" "net/http" + "path/filepath" + "sort" "strconv" + "time" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/api/handlers/utils" "github.com/gorilla/schema" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) func StopContainer(w http.ResponseWriter, r *http.Request) { @@ -46,7 +50,8 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) { } func ListContainers(w http.ResponseWriter, r *http.Request) { var ( - filters []string + filterFuncs []libpod.ContainerFilter + pss []ListContainer ) decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { @@ -73,20 +78,55 @@ func ListContainers(w http.ResponseWriter, r *http.Request) { Size: query.Size, Sort: "", Namespace: query.Namespace, + NoTrunc: true, Pod: query.Pod, Sync: query.Sync, } if len(query.Filter) > 0 { for k, v := range query.Filter { for _, val := range v { - filters = append(filters, fmt.Sprintf("%s=%s", k, val)) + generatedFunc, err := shared.GenerateContainerFilterFuncs(k, val, runtime) + if err != nil { + utils.InternalServerError(w, err) + return + } + filterFuncs = append(filterFuncs, generatedFunc) } } } - pss, err := shared.GetPsContainerOutput(runtime, opts, filters, 2) + + if !query.All { + // The default is get only running containers. Do this with a filterfunc + runningOnly, err := shared.GenerateContainerFilterFuncs("status", define.ContainerStateRunning.String(), runtime) + if err != nil { + utils.InternalServerError(w, err) + return + } + filterFuncs = append(filterFuncs, runningOnly) + } + + cons, err := runtime.GetContainers(filterFuncs...) if err != nil { utils.InternalServerError(w, err) } + if query.Last > 0 { + // Sort the containers we got + sort.Sort(psSortCreateTime{cons}) + // we should perform the lopping before we start getting + // the expensive information on containers + if query.Last < len(cons) { + cons = cons[len(cons)-query.Last:] + } + } + for _, con := range cons { + listCon, err := ListContainerBatch(runtime, con, opts) + if err != nil { + utils.InternalServerError(w, err) + return + } + pss = append(pss, listCon) + + } utils.WriteResponse(w, http.StatusOK, pss) } @@ -194,3 +234,122 @@ func ShowMountedContainers(w http.ResponseWriter, r *http.Request) { } utils.WriteResponse(w, http.StatusOK, response) } + +// BatchContainerOp is used in ps to reduce performance hits by "batching" +// locks. +func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts shared.PsOptions) (ListContainer, error) { + var ( + conConfig *libpod.ContainerConfig + conState define.ContainerStatus + err error + exitCode int32 + exited bool + pid int + size *shared.ContainerSize + startedTime time.Time + exitedTime time.Time + cgroup, ipc, mnt, net, pidns, user, uts string + ) + + batchErr := ctr.Batch(func(c *libpod.Container) error { + conConfig = c.Config() + conState, err = c.State() + if err != nil { + return errors.Wrapf(err, "unable to obtain container state") + } + + exitCode, exited, err = c.ExitCode() + if err != nil { + return errors.Wrapf(err, "unable to obtain container exit code") + } + startedTime, err = c.StartedTime() + if err != nil { + logrus.Errorf("error getting started time for %q: %v", c.ID(), err) + } + exitedTime, err = c.FinishedTime() + if err != nil { + logrus.Errorf("error getting exited time for %q: %v", c.ID(), err) + } + + if !opts.Size && !opts.Namespace { + return nil + } + + if opts.Namespace { + pid, err = c.PID() + if err != nil { + return errors.Wrapf(err, "unable to obtain container pid") + } + ctrPID := strconv.Itoa(pid) + cgroup, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "cgroup")) + ipc, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "ipc")) + mnt, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "mnt")) + net, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "net")) + pidns, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "pid")) + user, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "user")) + uts, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "uts")) + } + if opts.Size { + size = new(shared.ContainerSize) + + rootFsSize, err := c.RootFsSize() + if err != nil { + logrus.Errorf("error getting root fs size for %q: %v", c.ID(), err) + } + + rwSize, err := c.RWSize() + if err != nil { + logrus.Errorf("error getting rw size for %q: %v", c.ID(), err) + } + + size.RootFsSize = rootFsSize + size.RwSize = rwSize + } + return nil + }) + + if batchErr != nil { + return ListContainer{}, batchErr + } + + ps := ListContainer{ + Command: conConfig.Command, + Created: conConfig.CreatedTime.Unix(), + Exited: exited, + ExitCode: exitCode, + ExitedAt: exitedTime.Unix(), + ID: conConfig.ID, + Image: conConfig.RootfsImageName, + IsInfra: conConfig.IsInfra, + Labels: conConfig.Labels, + Mounts: ctr.UserVolumes(), + Names: []string{conConfig.Name}, + Pid: pid, + Pod: conConfig.Pod, + Ports: conConfig.PortMappings, + Size: size, + StartedAt: startedTime.Unix(), + State: conState.String(), + } + if opts.Pod && len(conConfig.Pod) > 0 { + pod, err := rt.GetPod(conConfig.Pod) + if err != nil { + return ListContainer{}, err + } + ps.PodName = pod.Name() + } + + if opts.Namespace { + ns := ListContainerNamespaces{ + Cgroup: cgroup, + IPC: ipc, + MNT: mnt, + NET: net, + PIDNS: pidns, + User: user, + UTS: uts, + } + ps.Namespaces = ns + } + return ps, nil +} diff --git a/pkg/api/handlers/libpod/swagger.go b/pkg/api/handlers/libpod/swagger.go new file mode 100644 index 000000000..aec30ef56 --- /dev/null +++ b/pkg/api/handlers/libpod/swagger.go @@ -0,0 +1,8 @@ +package libpod + +// List Containers +// swagger:response ListContainers +type swagInspectPodResponse struct { + // in:body + Body []ListContainer +} diff --git a/pkg/api/handlers/libpod/types.go b/pkg/api/handlers/libpod/types.go new file mode 100644 index 000000000..0949b2a72 --- /dev/null +++ b/pkg/api/handlers/libpod/types.go @@ -0,0 +1,82 @@ +package libpod + +import ( + "github.com/containers/libpod/cmd/podman/shared" + "github.com/containers/libpod/libpod" + "github.com/cri-o/ocicni/pkg/ocicni" +) + +// Listcontainer describes a container suitable for listing +type ListContainer struct { + // Container command + Command []string + // Container creation time + Created int64 + // If container has exited/stopped + Exited bool + // Time container exited + ExitedAt int64 + // If container has exited, the return code from the command + ExitCode int32 + // The unique identifier for the container + ID string `json:"Id"` + // Container image + Image string + // If this container is a Pod infra container + IsInfra bool + // Labels for container + Labels map[string]string + // User volume mounts + Mounts []string + // The names assigned to the container + Names []string + // Namespaces the container belongs to. Requires the + // namespace boolean to be true + Namespaces ListContainerNamespaces + // The process id of the container + Pid int + // If the container is part of Pod, the Pod ID. Requires the pod + // boolean to be set + Pod string + // If the container is part of Pod, the Pod name. Requires the pod + // boolean to be set + PodName string + // Port mappings + Ports []ocicni.PortMapping + // Size of the container rootfs. Requires the size boolean to be true + Size *shared.ContainerSize + // Time when container started + StartedAt int64 + // State of container + State string +} + +// ListContainer Namespaces contains the identifiers of the container's Linux namespaces +type ListContainerNamespaces struct { + // Mount namespace + MNT string `json:"Mnt,omitempty"` + // Cgroup namespace + Cgroup string `json:"Cgroup,omitempty"` + // IPC namespace + IPC string `json:"Ipc,omitempty"` + // Network namespace + NET string `json:"Net,omitempty"` + // PID namespace + PIDNS string `json:"Pidns,omitempty"` + // UTS namespace + UTS string `json:"Uts,omitempty"` + // User namespace + User string `json:"User,omitempty"` +} + +// sortContainers helps us set-up ability to sort by createTime +type sortContainers []*libpod.Container + +func (a sortContainers) Len() int { return len(a) } +func (a sortContainers) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +type psSortCreateTime struct{ sortContainers } + +func (a psSortCreateTime) Less(i, j int) bool { + return a.sortContainers[i].CreatedTime().Before(a.sortContainers[j].CreatedTime()) +} diff --git a/pkg/api/handlers/swagger.go b/pkg/api/handlers/swagger.go index bc75777aa..10525bfc7 100644 --- a/pkg/api/handlers/swagger.go +++ b/pkg/api/handlers/swagger.go @@ -1,7 +1,6 @@ package handlers import ( - "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/inspect" @@ -104,13 +103,6 @@ type swagDockerTopResponse struct { } } -// List containers -// swagger:response LibpodListContainersResponse -type swagLibpodListContainersResponse struct { - // in:body - Body []shared.PsContainerOutput -} - // Inspect container // swagger:response LibpodInspectContainerResponse type swagLibpodInspectContainerResponse struct { diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index abae81c38..ed30bc14a 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -610,7 +610,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // - application/json // responses: // 200: - // $ref: "#/responses/LibpodListContainersResponse" + // $ref: "#/responses/ListContainers" // 400: // $ref: "#/responses/BadParamError" // 500: diff --git a/test/e2e/commit_test.go b/test/e2e/commit_test.go index e9d274649..d4503d5a8 100644 --- a/test/e2e/commit_test.go +++ b/test/e2e/commit_test.go @@ -115,6 +115,25 @@ var _ = Describe("Podman commit", func() { Expect(foundBlue).To(Equal(true)) }) + It("podman commit container with change flag and JSON entrypoint with =", func() { + test := podmanTest.Podman([]string{"run", "--name", "test1", "-d", ALPINE, "ls"}) + test.WaitWithDefaultTimeout() + Expect(test.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainers()).To(Equal(1)) + + session := podmanTest.Podman([]string{"commit", "--change", `ENTRYPOINT ["foo", "bar=baz"]`, "test1", "foobar.com/test1-image:latest"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + check := podmanTest.Podman([]string{"inspect", "foobar.com/test1-image:latest"}) + check.WaitWithDefaultTimeout() + data := check.InspectImageJSON() + Expect(len(data)).To(Equal(1)) + Expect(len(data[0].Config.Entrypoint)).To(Equal(2)) + Expect(data[0].Config.Entrypoint[0]).To(Equal("foo")) + Expect(data[0].Config.Entrypoint[1]).To(Equal("bar=baz")) + }) + It("podman commit container with change CMD flag", func() { test := podmanTest.Podman([]string{"run", "--name", "test1", "-d", ALPINE, "ls"}) test.WaitWithDefaultTimeout() |