diff options
36 files changed, 238 insertions, 110 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 5a4815e1c..5b0d9ee6c 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -351,14 +351,13 @@ static_alt_build_task: ALT_NAME: 'Static build' # Do not use 'latest', fixed-version tag for runtime stability. CTR_FQIN: "docker.io/nixos/nix:2.3.6" - # This is critical, it helps to avoid a very lengthy process of - # statically building every dependency needed to build podman. - # Assuming the dependency and build description hasn't changed, - # this cache ensures only the static podman binary is built. - nix_cache: - folder: '/var/cache/nix' - # Cirrus will calculate/use sha of this output as the cache key - fingerprint_script: echo "${IMAGE_SUFFIX}" && cat nix/* + # Authentication token for pushing the build cache to cachix. + # This is critical, it helps to avoid a very lengthy process of + # statically building every dependency needed to build podman. + # Assuming the pinned nix dependencies in nix/nixpkgs.json have not + # changed, this cache will ensure that only the static podman binary is + # built. + CACHIX_AUTH_TOKEN: ENCRYPTED[df0d4d0a67474e8ea49cc503221dcb912b7e2ba45c8ec4bf2e5fd9c49a18ac21c24bacee59b5393355ed9e4358d2baef] setup_script: *setup main_script: *main always: *binary_artifacts diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index d23771fc5..31f44d92f 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -78,7 +78,7 @@ func listFlagSet(cmd *cobra.Command) { flags := cmd.Flags() flags.BoolVarP(&listOpts.All, "all", "a", false, "Show all the containers, default is only running containers") - flags.BoolVar(&listOpts.Storage, "external", false, "Show containers in storage not controlled by Podman") + flags.BoolVar(&listOpts.External, "external", false, "Show containers in storage not controlled by Podman") filterFlagName := "filter" flags.StringSliceVarP(&filters, filterFlagName, "f", []string{}, "Filter output based on conditions given") @@ -132,10 +132,10 @@ func checkFlags(c *cobra.Command) error { } cfg := registry.PodmanConfig() if cfg.Engine.Namespace != "" { - if c.Flag("storage").Changed && listOpts.Storage { - return errors.New("--namespace and --storage flags can not both be set") + if c.Flag("storage").Changed && listOpts.External { + return errors.New("--namespace and --external flags can not both be set") } - listOpts.Storage = false + listOpts.External = false } return nil diff --git a/cmd/podman/containers/rm.go b/cmd/podman/containers/rm.go index ea616b6e5..884ad05f4 100644 --- a/cmd/podman/containers/rm.go +++ b/cmd/podman/containers/rm.go @@ -140,6 +140,10 @@ func removeContainers(namesOrIDs []string, rmOptions entities.RmOptions, setExit } func setExitCode(err error) { + // If error is set to no such container, do not reset + if registry.GetExitCode() == 1 { + return + } cause := errors.Cause(err) switch { case cause == define.ErrNoSuchCtr: diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go index 4219e325b..957c0ac2d 100644 --- a/cmd/podman/images/build.go +++ b/cmd/podman/images/build.go @@ -266,7 +266,7 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil pullPolicy := imagebuildah.PullIfMissing if c.Flags().Changed("pull") && flags.Pull { - pullPolicy = imagebuildah.PullAlways + pullPolicy = imagebuildah.PullIfNewer } if flags.PullAlways { pullPolicy = imagebuildah.PullAlways diff --git a/contrib/cirrus/required_host_ports.txt b/contrib/cirrus/required_host_ports.txt index 9248e497a..5f066e059 100644 --- a/contrib/cirrus/required_host_ports.txt +++ b/contrib/cirrus/required_host_ports.txt @@ -2,3 +2,4 @@ github.com 22 docker.io 443 quay.io 443 registry.fedoraproject.org 443 +podman.cachix.org 443 diff --git a/contrib/cirrus/runner.sh b/contrib/cirrus/runner.sh index 50bc1102f..3292cea84 100755 --- a/contrib/cirrus/runner.sh +++ b/contrib/cirrus/runner.sh @@ -241,15 +241,14 @@ function _run_altbuild() { req_env_vars CTR_FQIN [[ "$UID" -eq 0 ]] || \ die "Static build must execute nixos container as root on host" - mkdir -p /var/cache/nix - podman run -i --rm -v /var/cache/nix:/mnt/nix:Z \ - $CTR_FQIN cp -rfT /nix /mnt/nix - podman run -i --rm -v /var/cache/nix:/nix:Z \ - -v $PWD:$PWD:Z -w $PWD $CTR_FQIN \ - nix --print-build-logs --option cores 4 --option max-jobs 4 \ - build --file ./nix/ - # result symlink is absolute from container perspective :( - cp /var/cache/$(readlink result)/bin/podman ./ # for cirrus-ci artifact + podman run -i --rm \ + -e CACHIX_AUTH_TOKEN \ + -v $PWD:$PWD:Z -w $PWD $CTR_FQIN sh -c \ + "nix-env -iA cachix -f https://cachix.org/api/v1/install && \ + cachix use podman && \ + nix-build nix && \ + nix-store -qR --include-outputs \$(nix-instantiate nix/default.nix) | grep -v podman | cachix push podman && \ + cp -R result/bin ." rm result # makes cirrus puke ;; *) diff --git a/contrib/rootless-cni-infra/Containerfile b/contrib/rootless-cni-infra/Containerfile index 871e06a6c..4324f39d2 100644 --- a/contrib/rootless-cni-infra/Containerfile +++ b/contrib/rootless-cni-infra/Containerfile @@ -2,7 +2,7 @@ ARG GOLANG_VERSION=1.15 ARG ALPINE_VERSION=3.12 ARG CNI_VERSION=v0.8.0 ARG CNI_PLUGINS_VERSION=v0.8.7 -ARG DNSNAME_VERSION=v1.0.0 +ARG DNSNAME_VERSION=v1.1.1 FROM golang:${GOLANG_VERSION}-alpine${ALPINE_VERSION} AS golang-base RUN apk add --no-cache git @@ -33,4 +33,4 @@ COPY rootless-cni-infra /usr/local/bin ENV CNI_PATH=/opt/cni/bin CMD ["sleep", "infinity"] -ENV ROOTLESS_CNI_INFRA_VERSION=3 +ENV ROOTLESS_CNI_INFRA_VERSION=5 diff --git a/contrib/rootless-cni-infra/rootless-cni-infra b/contrib/rootless-cni-infra/rootless-cni-infra index 463254c7f..cceb8d817 100755 --- a/contrib/rootless-cni-infra/rootless-cni-infra +++ b/contrib/rootless-cni-infra/rootless-cni-infra @@ -21,16 +21,19 @@ wait_unshare_net() { done } -# CLI subcommand: "alloc $CONTAINER_ID $NETWORK_NAME $POD_NAME" +# CLI subcommand: "alloc $CONTAINER_ID $NETWORK_NAME $POD_NAME $IP $MAC $CAP_ARGS" cmd_entrypoint_alloc() { - if [ "$#" -ne 3 ]; then - echo >&2 "Usage: $ARG0 alloc CONTAINER_ID NETWORK_NAME POD_NAME" + if [ "$#" -ne 6 ]; then + echo >&2 "Usage: $ARG0 alloc CONTAINER_ID NETWORK_NAME POD_NAME IP MAC CAP_ARGS" exit 1 fi ID="$1" NET="$2" K8S_POD_NAME="$3" + IP="$4" + MAC="$5" + CAP_ARGS="$6" dir="${BASE}/${ID}" mkdir -p "${dir}/attached" "${dir}/attached-args" @@ -46,9 +49,18 @@ cmd_entrypoint_alloc() { nsenter -t "${pid}" -n ip link set lo up fi CNI_ARGS="IgnoreUnknown=1;K8S_POD_NAME=${K8S_POD_NAME}" + if [ "$IP" ]; then + CNI_ARGS="$CNI_ARGS;IP=${IP}" + fi + if [ "$MAC" ]; then + CNI_ARGS="$CNI_ARGS;MAC=${MAC}" + fi + if [ "$CAP_ARGS" ]; then + CAP_ARGS="$CAP_ARGS" + fi nwcount=$(find "${dir}/attached" -type f | wc -l) CNI_IFNAME="eth${nwcount}" - export CNI_ARGS CNI_IFNAME + export CNI_ARGS CNI_IFNAME CAP_ARGS cnitool add "${NET}" "/proc/${pid}/ns/net" >"${dir}/attached/${NET}" echo "${CNI_ARGS}" >"${dir}/attached-args/${NET}" diff --git a/libpod/oci_conmon_exec_linux.go b/libpod/oci_conmon_exec_linux.go index dc5dd03df..faf86ea5b 100644 --- a/libpod/oci_conmon_exec_linux.go +++ b/libpod/oci_conmon_exec_linux.go @@ -126,20 +126,25 @@ func (r *ConmonOCIRuntime) ExecContainerHTTP(ctr *Container, sessionID string, o }() attachChan := make(chan error) + conmonPipeDataChan := make(chan conmonPipeData) go func() { // attachToExec is responsible for closing pipes - attachChan <- attachExecHTTP(ctr, sessionID, req, w, streams, pipes, detachKeys, options.Terminal, cancel, hijackDone, holdConnOpen) + attachChan <- attachExecHTTP(ctr, sessionID, req, w, streams, pipes, detachKeys, options.Terminal, cancel, hijackDone, holdConnOpen, execCmd, conmonPipeDataChan, ociLog) close(attachChan) }() - // Wait for conmon to succeed, when return. - if err := execCmd.Wait(); err != nil { - return -1, nil, errors.Wrapf(err, "cannot run conmon") - } + // NOTE: the channel is needed to communicate conmon's data. In case + // of an error, the error will be written on the hijacked http + // connection such that remote clients will receive the error. + pipeData := <-conmonPipeDataChan - pid, err := readConmonPipeData(pipes.syncPipe, ociLog) + return pipeData.pid, attachChan, pipeData.err +} - return pid, attachChan, err +// conmonPipeData contains the data when reading from conmon's pipe. +type conmonPipeData struct { + pid int + err error } // ExecContainerDetached executes a command in a running container, but does @@ -488,9 +493,16 @@ func (r *ConmonOCIRuntime) startExec(c *Container, sessionID string, options *Ex } // Attach to a container over HTTP -func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, pipes *execPipes, detachKeys []byte, isTerminal bool, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool) (deferredErr error) { +func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, pipes *execPipes, detachKeys []byte, isTerminal bool, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, execCmd *exec.Cmd, conmonPipeDataChan chan<- conmonPipeData, ociLog string) (deferredErr error) { + // NOTE: As you may notice, the attach code is quite complex. + // Many things happen concurrently and yet are interdependent. + // If you ever change this function, make sure to write to the + // conmonPipeDataChan in case of an error. + if pipes == nil || pipes.startPipe == nil || pipes.attachPipe == nil { - return errors.Wrapf(define.ErrInvalidArg, "must provide a start and attach pipe to finish an exec attach") + err := errors.Wrapf(define.ErrInvalidArg, "must provide a start and attach pipe to finish an exec attach") + conmonPipeDataChan <- conmonPipeData{-1, err} + return err } defer func() { @@ -509,17 +521,20 @@ func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.Resp // set up the socket path, such that it is the correct length and location for exec sockPath, err := c.execAttachSocketPath(sessionID) if err != nil { + conmonPipeDataChan <- conmonPipeData{-1, err} return err } // 2: read from attachFd that the parent process has set up the console socket if _, err := readConmonPipeData(pipes.attachPipe, ""); err != nil { + conmonPipeDataChan <- conmonPipeData{-1, err} return err } // 2: then attach conn, err := openUnixSocket(sockPath) if err != nil { + conmonPipeDataChan <- conmonPipeData{-1, err} return errors.Wrapf(err, "failed to connect to container's attach socket: %v", sockPath) } defer func() { @@ -540,11 +555,13 @@ func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.Resp // Perform hijack hijacker, ok := w.(http.Hijacker) if !ok { + conmonPipeDataChan <- conmonPipeData{-1, err} return errors.Errorf("unable to hijack connection") } httpCon, httpBuf, err := hijacker.Hijack() if err != nil { + conmonPipeDataChan <- conmonPipeData{-1, err} return errors.Wrapf(err, "error hijacking connection") } @@ -555,10 +572,23 @@ func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.Resp // Force a flush after the header is written. if err := httpBuf.Flush(); err != nil { + conmonPipeDataChan <- conmonPipeData{-1, err} return errors.Wrapf(err, "error flushing HTTP hijack header") } go func() { + // Wait for conmon to succeed, when return. + if err := execCmd.Wait(); err != nil { + conmonPipeDataChan <- conmonPipeData{-1, err} + } else { + pid, err := readConmonPipeData(pipes.syncPipe, ociLog) + if err != nil { + hijackWriteError(err, c.ID(), isTerminal, httpBuf) + conmonPipeDataChan <- conmonPipeData{pid, err} + } else { + conmonPipeDataChan <- conmonPipeData{pid, err} + } + } // We need to hold the connection open until the complete exec // function has finished. This channel will be closed in a defer // in that function, so we can wait for it here. diff --git a/libpod/rootless_cni_linux.go b/libpod/rootless_cni_linux.go index 9a980750f..94ae062aa 100644 --- a/libpod/rootless_cni_linux.go +++ b/libpod/rootless_cni_linux.go @@ -25,7 +25,7 @@ import ( // Built from ../contrib/rootless-cni-infra. var rootlessCNIInfraImage = map[string]string{ - "amd64": "quay.io/libpod/rootless-cni-infra@sha256:304742d5d221211df4ec672807a5842ff11e3729c50bc424ea0cea858f69d7b7", // 3-amd64 + "amd64": "quay.io/libpod/rootless-cni-infra@sha256:adf352454666f7ce9ca3e1098448b5ee18f89c4516471ec99447ec9ece917f36", // 5-amd64 } const ( @@ -58,9 +58,33 @@ func AllocRootlessCNI(ctx context.Context, c *Container) (ns.NetNS, []*cnitypes. return nil, nil, err } k8sPodName := getCNIPodName(c) // passed to CNI as K8S_POD_NAME + ip := "" + if c.config.StaticIP != nil { + ip = c.config.StaticIP.String() + } + mac := "" + if c.config.StaticMAC != nil { + mac = c.config.StaticMAC.String() + } + aliases, err := c.runtime.state.GetAllNetworkAliases(c) + if err != nil { + return nil, nil, err + } + capArgs := "" + // add network aliases json encoded as capabilityArgs for cni + if len(aliases) > 0 { + capabilityArgs := make(map[string]interface{}) + capabilityArgs["aliases"] = aliases + b, err := json.Marshal(capabilityArgs) + if err != nil { + return nil, nil, err + } + capArgs = string(b) + } + cniResults := make([]*cnitypes.Result, len(networks)) for i, nw := range networks { - cniRes, err := rootlessCNIInfraCallAlloc(infra, c.ID(), nw, k8sPodName) + cniRes, err := rootlessCNIInfraCallAlloc(infra, c.ID(), nw, k8sPodName, ip, mac, capArgs) if err != nil { return nil, nil, err } @@ -137,11 +161,11 @@ func getCNIPodName(c *Container) string { return c.Name() } -func rootlessCNIInfraCallAlloc(infra *Container, id, nw, k8sPodName string) (*cnitypes.Result, error) { - logrus.Debugf("rootless CNI: alloc %q, %q, %q", id, nw, k8sPodName) +func rootlessCNIInfraCallAlloc(infra *Container, id, nw, k8sPodName, ip, mac, capArgs string) (*cnitypes.Result, error) { + logrus.Debugf("rootless CNI: alloc %q, %q, %q, %q, %q, %q", id, nw, k8sPodName, ip, mac, capArgs) var err error - _, err = rootlessCNIInfraExec(infra, "alloc", id, nw, k8sPodName) + _, err = rootlessCNIInfraExec(infra, "alloc", id, nw, k8sPodName, ip, mac, capArgs) if err != nil { return nil, err } diff --git a/libpod/util.go b/libpod/util.go index bf9bf2542..391208fb9 100644 --- a/libpod/util.go +++ b/libpod/util.go @@ -235,20 +235,16 @@ func checkDependencyContainer(depCtr, ctr *Container) error { return nil } -// hijackWriteErrorAndClose writes an error to a hijacked HTTP session and -// closes it. Intended to HTTPAttach function. -// If error is nil, it will not be written; we'll only close the connection. -func hijackWriteErrorAndClose(toWrite error, cid string, terminal bool, httpCon io.Closer, httpBuf *bufio.ReadWriter) { +// hijackWriteError writes an error to a hijacked HTTP session. +func hijackWriteError(toWrite error, cid string, terminal bool, httpBuf *bufio.ReadWriter) { if toWrite != nil { - errString := []byte(fmt.Sprintf("%v\n", toWrite)) + errString := []byte(fmt.Sprintf("Error: %v\n", toWrite)) if !terminal { // We need a header. header := makeHTTPAttachHeader(2, uint32(len(errString))) if _, err := httpBuf.Write(header); err != nil { logrus.Errorf("Error writing header for container %s attach connection error: %v", cid, err) } - // TODO: May want to return immediately here to avoid - // writing garbage to the socket? } if _, err := httpBuf.Write(errString); err != nil { logrus.Errorf("Error writing error to container %s HTTP attach connection: %v", cid, err) @@ -257,6 +253,13 @@ func hijackWriteErrorAndClose(toWrite error, cid string, terminal bool, httpCon logrus.Errorf("Error flushing HTTP buffer for container %s HTTP attach connection: %v", cid, err) } } +} + +// hijackWriteErrorAndClose writes an error to a hijacked HTTP session and +// closes it. Intended to HTTPAttach function. +// If error is nil, it will not be written; we'll only close the connection. +func hijackWriteErrorAndClose(toWrite error, cid string, terminal bool, httpCon io.Closer, httpBuf *bufio.ReadWriter) { + hijackWriteError(toWrite, cid, terminal, httpBuf) if err := httpCon.Close(); err != nil { logrus.Errorf("Error closing container %s HTTP attach connection: %v", cid, err) diff --git a/nix/nixpkgs.json b/nix/nixpkgs.json index d304de536..0cfb251f2 100644 --- a/nix/nixpkgs.json +++ b/nix/nixpkgs.json @@ -1,9 +1,9 @@ { "url": "https://github.com/nixos/nixpkgs", - "rev": "4a75203f0270f96cbc87f5dfa5d5185690237d87", - "date": "2020-12-29T03:18:48+01:00", - "path": "/nix/store/scswsm6r4jnhp9ki0f6s81kpj5x6jkn7-nixpkgs", - "sha256": "0h70fm9aa7s06wkalbadw70z5rscbs3p6nblb47z523nhlzgjxk9", + "rev": "ce7b327a52d1b82f82ae061754545b1c54b06c66", + "date": "2021-01-25T11:28:05+01:00", + "path": "/nix/store/dpsa6a1sy8hwhwjkklc52brs9z1k5fx9-nixpkgs", + "sha256": "1rc4if8nmy9lrig0ddihdwpzg2s8y36vf20hfywb8hph5hpsg4vj", "fetchSubmodules": false, "deepClone": false, "leaveDotGit": false diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index aa12afc82..0f91a4362 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -73,7 +73,7 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } - if report[0].Err != nil { + if len(report) > 0 && report[0].Err != nil { utils.InternalServerError(w, report[0].Err) return } diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go index 6b07b1cc5..a0cb1d49e 100644 --- a/pkg/api/handlers/libpod/containers.go +++ b/pkg/api/handlers/libpod/containers.go @@ -12,7 +12,6 @@ import ( "github.com/containers/podman/v2/pkg/api/handlers/utils" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/domain/infra/abi" - "github.com/containers/podman/v2/pkg/ps" "github.com/gorilla/schema" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -63,6 +62,7 @@ func ListContainers(w http.ResponseWriter, r *http.Request) { decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { All bool `schema:"all"` + External bool `schema:"external"` Filters map[string][]string `schema:"filters"` Last int `schema:"last"` // alias for limit Limit int `schema:"limit"` @@ -90,17 +90,22 @@ func ListContainers(w http.ResponseWriter, r *http.Request) { } runtime := r.Context().Value("runtime").(*libpod.Runtime) + // Now use the ABI implementation to prevent us from having duplicate + // code. + containerEngine := abi.ContainerEngine{Libpod: runtime} opts := entities.ContainerListOptions{ All: query.All, + External: query.External, Filters: query.Filters, Last: limit, - Size: query.Size, - Sort: "", Namespace: query.Namespace, - Pod: true, - Sync: query.Sync, + // Always return Pod, should not be part of the API. + // https://github.com/containers/podman/pull/7223 + Pod: true, + Size: query.Size, + Sync: query.Sync, } - pss, err := ps.GetContainerLists(runtime, opts) + pss, err := containerEngine.ContainerList(r.Context(), opts) if err != nil { utils.InternalServerError(w, err) return diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index 74a04b2e6..e30747800 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -48,6 +48,11 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // default: false // description: Return all containers. By default, only running containers are shown // - in: query + // name: external + // type: boolean + // default: false + // description: Return containers in storage not controlled by Podman + // - in: query // name: limit // description: Return this number of most recently created containers, including non-running ones. // type: integer diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go index 24604fa83..3fb1ab733 100644 --- a/pkg/bindings/containers/types.go +++ b/pkg/bindings/containers/types.go @@ -106,6 +106,7 @@ type MountedContainerPathsOptions struct{} // ListOptions are optional options for listing containers type ListOptions struct { All *bool + External *bool Filters map[string][]string Last *int Namespace *bool diff --git a/pkg/bindings/containers/types_list_options.go b/pkg/bindings/containers/types_list_options.go index 43326fa59..c363dcd32 100644 --- a/pkg/bindings/containers/types_list_options.go +++ b/pkg/bindings/containers/types_list_options.go @@ -103,6 +103,22 @@ func (o *ListOptions) GetAll() bool { return *o.All } +// WithExternal +func (o *ListOptions) WithExternal(value bool) *ListOptions { + v := &value + o.External = v + return o +} + +// GetExternal +func (o *ListOptions) GetExternal() bool { + var external bool + if o.External == nil { + return external + } + return *o.External +} + // WithFilters func (o *ListOptions) WithFilters(value map[string][]string) *ListOptions { v := value diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 4c1bd6a7d..2c32f792f 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -297,8 +297,8 @@ type ContainerListOptions struct { Pod bool Quiet bool Size bool + External bool Sort string - Storage bool Sync bool Watch uint } diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 524b29553..0c61714c3 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -173,19 +173,32 @@ func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []st } func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, opts entities.RmOptions) ([]*entities.RmReport, error) { - ctrs, err := getContainersByContext(ic.ClientCtx, opts.All, opts.Ignore, namesOrIds) - if err != nil { - return nil, err - } // TODO there is no endpoint for container eviction. Need to discuss - reports := make([]*entities.RmReport, 0, len(ctrs)) - options := new(containers.RemoveOptions).WithForce(opts.Force).WithVolumes(opts.Volumes) - for _, c := range ctrs { + options := new(containers.RemoveOptions).WithForce(opts.Force).WithVolumes(opts.Volumes).WithIgnore(opts.Ignore) + + if opts.All { + ctrs, err := getContainersByContext(ic.ClientCtx, opts.All, opts.Ignore, namesOrIds) + if err != nil { + return nil, err + } + reports := make([]*entities.RmReport, 0, len(ctrs)) + for _, c := range ctrs { + reports = append(reports, &entities.RmReport{ + Id: c.ID, + Err: containers.Remove(ic.ClientCtx, c.ID, options), + }) + } + return reports, nil + } + + reports := make([]*entities.RmReport, 0, len(namesOrIds)) + for _, name := range namesOrIds { reports = append(reports, &entities.RmReport{ - Id: c.ID, - Err: containers.Remove(ic.ClientCtx, c.ID, options), + Id: name, + Err: containers.Remove(ic.ClientCtx, name, options), }) } + return reports, nil } @@ -601,7 +614,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri func (ic *ContainerEngine) ContainerList(ctx context.Context, opts entities.ContainerListOptions) ([]entities.ListContainer, error) { options := new(containers.ListOptions).WithFilters(opts.Filters).WithAll(opts.All).WithLast(opts.Last) - options.WithNamespace(opts.Namespace).WithSize(opts.Size).WithSync(opts.Sync) + options.WithNamespace(opts.Namespace).WithSize(opts.Size).WithSync(opts.Sync).WithExternal(opts.External) return containers.List(ic.ClientCtx, options) } diff --git a/pkg/ps/ps.go b/pkg/ps/ps.go index dc577890a..42f9e1d39 100644 --- a/pkg/ps/ps.go +++ b/pkg/ps/ps.go @@ -69,7 +69,7 @@ func GetContainerLists(runtime *libpod.Runtime, options entities.ContainerListOp pss = append(pss, listCon) } - if options.All && options.Storage { + if options.All && options.External { externCons, err := runtime.StorageContainers() if err != nil { return nil, err diff --git a/pkg/specgen/container_validate.go b/pkg/specgen/container_validate.go index a0d36f865..81cb8b78d 100644 --- a/pkg/specgen/container_validate.go +++ b/pkg/specgen/container_validate.go @@ -30,7 +30,7 @@ func exclusiveOptions(opt1, opt2 string) error { // input for creating a container. func (s *SpecGenerator) Validate() error { - if rootless.IsRootless() { + if rootless.IsRootless() && len(s.CNINetworks) == 0 { if s.StaticIP != nil || s.StaticIPv6 != nil { return ErrNoStaticIPRootless } diff --git a/pkg/specgen/pod_validate.go b/pkg/specgen/pod_validate.go index 7c81f3f9f..518adb32f 100644 --- a/pkg/specgen/pod_validate.go +++ b/pkg/specgen/pod_validate.go @@ -20,7 +20,7 @@ func exclusivePodOptions(opt1, opt2 string) error { // Validate verifies the input is valid func (p *PodSpecGenerator) Validate() error { - if rootless.IsRootless() { + if rootless.IsRootless() && len(p.CNINetworks) == 0 { if p.StaticIP != nil { return ErrNoStaticIPRootless } diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index 2668b1e7b..781bbb6d2 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -10,6 +10,7 @@ import ( "sort" "strconv" "strings" + "sync" "testing" "time" @@ -84,6 +85,7 @@ type testResultsSortedLength struct{ testResultsSorted } func (a testResultsSorted) Less(i, j int) bool { return a[i].length < a[j].length } var testResults []testResult +var testResultsMutex sync.Mutex func TestMain(m *testing.M) { if reexec.Init() { @@ -349,7 +351,9 @@ func (p *PodmanTestIntegration) InspectContainer(name string) []define.InspectCo func processTestResult(f GinkgoTestDescription) { tr := testResult{length: f.Duration.Seconds(), name: f.TestText} + testResultsMutex.Lock() testResults = append(testResults, tr) + testResultsMutex.Unlock() } func GetPortLock(port string) storage.Locker { diff --git a/test/e2e/create_staticip_test.go b/test/e2e/create_staticip_test.go index 7a2267617..698bbf976 100644 --- a/test/e2e/create_staticip_test.go +++ b/test/e2e/create_staticip_test.go @@ -49,7 +49,7 @@ var _ = Describe("Podman create with --ip flag", func() { }) It("Podman create --ip with non-allocatable IP", func() { - SkipIfRootless("--ip is not supported in rootless mode") + SkipIfRootless("--ip not supported without network in rootless mode") result := podmanTest.Podman([]string{"create", "--name", "test", "--ip", "203.0.113.124", ALPINE, "ls"}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) @@ -63,7 +63,7 @@ var _ = Describe("Podman create with --ip flag", func() { ip := GetRandomIPAddress() result := podmanTest.Podman([]string{"create", "--name", "test", "--ip", ip, ALPINE, "ip", "addr"}) result.WaitWithDefaultTimeout() - // Rootless static ip assignment should error + // Rootless static ip assignment without network should error if rootless.IsRootless() { Expect(result.ExitCode()).To(Equal(125)) } else { @@ -81,7 +81,7 @@ var _ = Describe("Podman create with --ip flag", func() { }) It("Podman create two containers with the same IP", func() { - SkipIfRootless("--ip not supported in rootless mode") + SkipIfRootless("--ip not supported without network in rootless mode") ip := GetRandomIPAddress() result := podmanTest.Podman([]string{"create", "--name", "test1", "--ip", ip, ALPINE, "sleep", "999"}) result.WaitWithDefaultTimeout() diff --git a/test/e2e/create_staticmac_test.go b/test/e2e/create_staticmac_test.go index 1ac431da2..4c8f371a4 100644 --- a/test/e2e/create_staticmac_test.go +++ b/test/e2e/create_staticmac_test.go @@ -56,11 +56,7 @@ var _ = Describe("Podman run with --mac-address flag", func() { result := podmanTest.Podman([]string{"run", "--network", net, "--mac-address", "92:d0:c6:00:29:34", ALPINE, "ip", "addr"}) result.WaitWithDefaultTimeout() - if rootless.IsRootless() { - Expect(result.ExitCode()).To(Equal(125)) - } else { - Expect(result.ExitCode()).To(Equal(0)) - Expect(result.OutputToString()).To(ContainSubstring("92:d0:c6:00:29:34")) - } + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(ContainSubstring("92:d0:c6:00:29:34")) }) }) diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go index 73d92e5a0..67c08ac09 100644 --- a/test/e2e/create_test.go +++ b/test/e2e/create_test.go @@ -553,7 +553,7 @@ var _ = Describe("Podman create", func() { }) It("create container in pod with IP should fail", func() { - SkipIfRootless("Setting IP not supported in rootless mode") + SkipIfRootless("Setting IP not supported in rootless mode without network") name := "createwithstaticip" pod := podmanTest.RunTopContainerInPod("", "new:"+name) pod.WaitWithDefaultTimeout() @@ -565,7 +565,7 @@ var _ = Describe("Podman create", func() { }) It("create container in pod with mac should fail", func() { - SkipIfRootless("Setting MAC Address not supported in rootless mode") + SkipIfRootless("Setting MAC Address not supported in rootless mode without network") name := "createwithstaticmac" pod := podmanTest.RunTopContainerInPod("", "new:"+name) pod.WaitWithDefaultTimeout() diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go index e2080244b..2f5290c76 100644 --- a/test/e2e/network_test.go +++ b/test/e2e/network_test.go @@ -408,7 +408,6 @@ var _ = Describe("Podman network", func() { Expect(lines[1]).To(Equal(netName2)) }) It("podman network with multiple aliases", func() { - Skip("Until DNSName is updated on our CI images") var worked bool netName := "aliasTest" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", netName}) diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index be0a2f6f0..9c448a81e 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -233,7 +233,7 @@ var _ = Describe("Podman pod create", func() { ip := GetRandomIPAddress() podCreate := podmanTest.Podman([]string{"pod", "create", "--ip", ip, "--name", name}) podCreate.WaitWithDefaultTimeout() - // Rootless should error + // Rootless should error without network if rootless.IsRootless() { Expect(podCreate.ExitCode()).To(Equal(125)) } else { @@ -246,7 +246,7 @@ var _ = Describe("Podman pod create", func() { }) It("podman container in pod with IP address shares IP address", func() { - SkipIfRootless("Rootless does not support --ip") + SkipIfRootless("Rootless does not support --ip without network") podName := "test" ctrName := "testCtr" ip := GetRandomIPAddress() diff --git a/test/e2e/pod_inspect_test.go b/test/e2e/pod_inspect_test.go index 25212991d..fd9589afe 100644 --- a/test/e2e/pod_inspect_test.go +++ b/test/e2e/pod_inspect_test.go @@ -101,7 +101,7 @@ var _ = Describe("Podman pod inspect", func() { }) It("podman pod inspect outputs show correct MAC", func() { - SkipIfRootless("--mac-address is not supported in rootless mode") + SkipIfRootless("--mac-address is not supported in rootless mode without network") podName := "testPod" macAddr := "42:43:44:00:00:01" create := podmanTest.Podman([]string{"pod", "create", "--name", podName, "--mac-address", macAddr}) diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index 13701fc3b..d12534219 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -396,11 +396,14 @@ var _ = Describe("Podman ps", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"ps", "--pod", "--no-trunc"}) - + session = podmanTest.Podman([]string{"ps", "--no-trunc"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(Not(ContainSubstring(podid))) + session = podmanTest.Podman([]string{"ps", "--pod", "--no-trunc"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring(podid)) }) diff --git a/test/e2e/rm_test.go b/test/e2e/rm_test.go index ca142d7f3..4c50a61ef 100644 --- a/test/e2e/rm_test.go +++ b/test/e2e/rm_test.go @@ -132,7 +132,7 @@ var _ = Describe("Podman rm", func() { latest := "-l" if IsRemote() { - latest = "test1" + latest = cid } result := podmanTest.Podman([]string{"rm", latest}) result.WaitWithDefaultTimeout() diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index cbaae7186..ebea2132a 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -621,7 +621,6 @@ var _ = Describe("Podman run networking", func() { }) It("podman run in custom CNI network with --static-ip", func() { - SkipIfRootless("Rootless mode does not support --ip") netName := stringid.GenerateNonCryptoID() ipAddr := "10.25.30.128" create := podmanTest.Podman([]string{"network", "create", "--subnet", "10.25.30.0/24", netName}) @@ -633,10 +632,6 @@ var _ = Describe("Podman run networking", func() { run.WaitWithDefaultTimeout() Expect(run.ExitCode()).To(BeZero()) Expect(run.OutputToString()).To(ContainSubstring(ipAddr)) - - create = podmanTest.Podman([]string{"network", "rm", netName}) - create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(BeZero()) }) It("podman rootless fails custom CNI network with --uidmap", func() { @@ -658,7 +653,6 @@ var _ = Describe("Podman run networking", func() { }) It("podman run with new:pod and static-ip", func() { - SkipIfRootless("Rootless does not support --ip") netName := stringid.GenerateNonCryptoID() ipAddr := "10.25.40.128" podname := "testpod" diff --git a/test/e2e/run_staticip_test.go b/test/e2e/run_staticip_test.go index 8383b1812..aeb462ae9 100644 --- a/test/e2e/run_staticip_test.go +++ b/test/e2e/run_staticip_test.go @@ -19,7 +19,7 @@ var _ = Describe("Podman run with --ip flag", func() { ) BeforeEach(func() { - SkipIfRootless("rootless does not support --ip") + SkipIfRootless("rootless does not support --ip without network") tempdir, err = CreateTempDirInTempDir() if err != nil { os.Exit(1) diff --git a/test/system/040-ps.bats b/test/system/040-ps.bats index 0ae8b0ce0..ae27c479f 100644 --- a/test/system/040-ps.bats +++ b/test/system/040-ps.bats @@ -82,11 +82,10 @@ load helpers run_podman rm -a } -@test "podman ps -a --storage" { - skip_if_remote "ps --storage does not work over remote" +@test "podman ps -a --external" { # Setup: ensure that we have no hidden storage containers - run_podman ps --storage -a + run_podman ps --external -a is "${#lines[@]}" "1" "setup check: no storage containers at start of test" # Force a buildah timeout; this leaves a buildah container behind @@ -98,18 +97,18 @@ EOF run_podman ps -a is "${#lines[@]}" "1" "podman ps -a does not see buildah container" - run_podman ps --storage -a - is "${#lines[@]}" "2" "podman ps -a --storage sees buildah container" + run_podman ps --external -a + is "${#lines[@]}" "2" "podman ps -a --external sees buildah container" is "${lines[1]}" \ "[0-9a-f]\{12\} \+$IMAGE *buildah .* seconds ago .* storage .* ${PODMAN_TEST_IMAGE_NAME}-working-container" \ - "podman ps --storage" + "podman ps --external" cid="${lines[1]:0:12}" # 'rm -a' should be a NOP run_podman rm -a - run_podman ps --storage -a - is "${#lines[@]}" "2" "podman ps -a --storage sees buildah container" + run_podman ps --external -a + is "${#lines[@]}" "2" "podman ps -a --external sees buildah container" # We can't rm it without -f, but podman should issue a helpful message run_podman 2 rm "$cid" @@ -118,7 +117,7 @@ EOF # With -f, we can remove it. run_podman rm -f "$cid" - run_podman ps --storage -a + run_podman ps --external -a is "${#lines[@]}" "1" "storage container has been removed" } diff --git a/test/system/070-build.bats b/test/system/070-build.bats index 7a42a4c18..6b5bc68fb 100644 --- a/test/system/070-build.bats +++ b/test/system/070-build.bats @@ -29,6 +29,29 @@ EOF run_podman rmi -f build_test } +@test "podman build - basic test with --pull" { + rand_filename=$(random_string 20) + rand_content=$(random_string 50) + + run_podman tag $IMAGE localhost/localonly + + tmpdir=$PODMAN_TMPDIR/build-test + mkdir -p $tmpdir + dockerfile=$tmpdir/Dockerfile + cat >$dockerfile <<EOF +FROM localhost/localonly +RUN echo $rand_content > /$rand_filename +EOF + # With --pull, Podman would try to pull a newer image but use the local one + # if present. See #9111. + run_podman build --pull -t build_test $tmpdir + + run_podman run --rm build_test cat /$rand_filename + is "$output" "$rand_content" "reading generated file in image" + + run_podman rmi -f build_test localhost/localonly +} + @test "podman build - global runtime flags test" { skip_if_remote "--runtime-flag flag not supported for remote" diff --git a/test/system/075-exec.bats b/test/system/075-exec.bats index c028e16c9..badf44c49 100644 --- a/test/system/075-exec.bats +++ b/test/system/075-exec.bats @@ -6,8 +6,6 @@ load helpers @test "podman exec - basic test" { - skip_if_remote "FIXME: pending #7241" - rand_filename=$(random_string 20) rand_content=$(random_string 50) |