diff options
-rw-r--r-- | cmd/podman/common/completion.go | 4 | ||||
-rw-r--r-- | cmd/podman/containers/port.go | 2 | ||||
-rw-r--r-- | cmd/podman/containers/rename.go | 7 | ||||
-rw-r--r-- | docs/source/markdown/links/podman-container-rename.1 | 1 | ||||
-rw-r--r-- | pkg/bindings/README.md | 156 | ||||
-rw-r--r-- | pkg/bindings/containers/rename.go | 28 | ||||
-rw-r--r-- | pkg/bindings/containers/types.go | 7 | ||||
-rw-r--r-- | pkg/bindings/containers/types_rename_options.go | 104 | ||||
-rw-r--r-- | pkg/bindings/images/build.go | 138 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/containers.go | 2 | ||||
-rw-r--r-- | pkg/specgen/generate/oci.go | 39 | ||||
-rw-r--r-- | test/e2e/build_test.go | 216 | ||||
-rw-r--r-- | test/e2e/pod_stats_test.go | 5 | ||||
-rw-r--r-- | test/e2e/rename_test.go | 1 | ||||
-rw-r--r-- | test/e2e/run_ns_test.go | 8 |
15 files changed, 664 insertions, 54 deletions
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index d01842998..c9a3c5e94 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -517,8 +517,8 @@ func AutocompleteRunlabelCommand(cmd *cobra.Command, args []string, toComplete s return nil, cobra.ShellCompDirectiveDefault } -// AutocompletePortCommand - Autocomplete podman port command args. -func AutocompletePortCommand(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { +// AutocompleteContainerOneArg - Autocomplete containers as fist arg. +func AutocompleteContainerOneArg(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } diff --git a/cmd/podman/containers/port.go b/cmd/podman/containers/port.go index d59161149..4d3a5310d 100644 --- a/cmd/podman/containers/port.go +++ b/cmd/podman/containers/port.go @@ -26,7 +26,7 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, true, false) }, - ValidArgsFunction: common.AutocompletePortCommand, + ValidArgsFunction: common.AutocompleteContainerOneArg, Example: `podman port --all podman port ctrID 80/tcp podman port --latest 80`, diff --git a/cmd/podman/containers/rename.go b/cmd/podman/containers/rename.go index 9c94e6272..78bd3db83 100644 --- a/cmd/podman/containers/rename.go +++ b/cmd/podman/containers/rename.go @@ -16,7 +16,7 @@ var ( Long: renameDescription, RunE: rename, Args: cobra.ExactArgs(2), - ValidArgsFunction: common.AutocompletePortCommand, + ValidArgsFunction: common.AutocompleteContainerOneArg, Example: "podman rename containerA newName", } @@ -32,14 +32,13 @@ var ( ) func init() { - // TODO: Once bindings are done, add this to TunnelMode registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: renameCommand, }) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerRenameCommand, Parent: containerCmd, }) diff --git a/docs/source/markdown/links/podman-container-rename.1 b/docs/source/markdown/links/podman-container-rename.1 new file mode 100644 index 000000000..1e71c57b6 --- /dev/null +++ b/docs/source/markdown/links/podman-container-rename.1 @@ -0,0 +1 @@ +.so man1/podman-rename.1 diff --git a/pkg/bindings/README.md b/pkg/bindings/README.md new file mode 100644 index 000000000..149deda5e --- /dev/null +++ b/pkg/bindings/README.md @@ -0,0 +1,156 @@ +# Podman Golang bindings +The Podman Go bindings are a set of functions to allow developers to execute Podman operations from within their Go based application. The Go bindings +connect to a Podman service which can run locally or on a remote machine. You can perform many operations including pulling and listing images, starting, +stopping or inspecting containers. Currently, the Podman repository has bindings available for operations on images, containers, pods, +networks and manifests among others. + +## Quick Start +The bindings require that the Podman system service is running for the specified user. This can be done with systemd using the `systemctl` command or manually +by calling the service directly. + +### Starting the service with system +The command to start the Podman service differs slightly depending on the user that is running the service. For a rootful service, +start the service like this: +``` +# systemctl start podman.socket +``` +For a non-privileged, aka rootless, user, start the service like this: + +``` +$ systemctl start --user podman.socket +``` + +### Starting the service manually +It can be handy to run the system service manually. Doing so allows you to enable debug messaging. +``` +$ podman --log-level=debug system service -t0 +``` +If you do not provide a specific path for the socket, a default is provided. The location of that socket for +rootful connections is `/run/podman/podman.sock` and for rootless it is `/run/USERID#/podman/podman.sock`. For more +information about the Podman system service, see `man podman-system-service`. + +### Creating a connection +The first step for using the bindings is to create a connection to the socket. As mentioned earlier, the destination +of the socket depends on the user who owns it. In this case, a rootful connection is made. + +``` +import ( + "context" + "fmt" + "os" + + "github.com/containers/podman/v2/pkg/bindings" +) + +func main() { + conn, err := bindings.NewConnection(context.Background(), "unix://run/podman/podman.sock") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + +} +``` +The `conn` variable returned from the `bindings.NewConnection` function can then be used in subsequent function calls +to interact with containers. + +### Examples +The following examples build upon the connection example from above. They are all rootful connections as well. + +#### Inspect a container +The following example obtains the inspect information for a container named `foorbar` and then prints +the container's ID. Note the use of optional inspect options for size. +``` +import ( + "context" + "fmt" + "os" + + "github.com/containers/podman/v2/pkg/bindings" + "github.com/containers/podman/v2/pkg/bindings/containers" +) + +func main() { + conn, err := bindings.NewConnection(context.Background(), "unix://run/podman/podman.sock") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + inspectData, err := containers.Inspect(conn, "foobar", new(containers.InspectOptions).WithSize(true)) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + // Print the container ID + fmt.Println(inspectData.ID) +} +``` + +#### Pull an image +The following example pulls the image `quay.ioo/libpod/alpine_nginx` to the local image store. +``` +import ( + "context" + "fmt" + "os" + + "github.com/containers/podman/v2/pkg/bindings" + "github.com/containers/podman/v2/pkg/bindings/images" +) + +func main() { + conn, err := bindings.NewConnection(context.Background(), "unix://run/podman/podman.sock") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + _, err = images.Pull(conn, "quay.io/libpod/alpine_nginx", nil) + if err != nil { + fmt.Println(err) + os.Exit(1) + } +} + +``` + +#### Pull an image, create a container, and start the container +The following example pulls the `quay.io/libpod/alpine_nginx` image and then creates a container named `foobar` +from it. And finally, it starts the container. +``` +import ( + "context" + "fmt" + "os" + + "github.com/containers/podman/v2/pkg/bindings" + "github.com/containers/podman/v2/pkg/bindings/containers" + "github.com/containers/podman/v2/pkg/bindings/images" + "github.com/containers/podman/v2/pkg/specgen" +) + +func main() { + conn, err := bindings.NewConnection(context.Background(), "unix://run/podman/podman.sock") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + _, err = images.Pull(conn, "quay.io/libpod/alpine_nginx", nil) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + s := specgen.NewSpecGenerator("quay.io/libpod/alpine_nginx", false) + s.Name = "foobar" + createResponse, err := containers.CreateWithSpec(conn, s, nil) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + fmt.Println("Container created.") + if err := containers.Start(conn, createResponse.ID, nil); err != nil { + fmt.Println(err) + os.Exit(1) + } + fmt.Println("Container started.") +} +``` diff --git a/pkg/bindings/containers/rename.go b/pkg/bindings/containers/rename.go new file mode 100644 index 000000000..0e8c7f198 --- /dev/null +++ b/pkg/bindings/containers/rename.go @@ -0,0 +1,28 @@ +package containers + +import ( + "context" + "net/http" + + "github.com/containers/podman/v2/pkg/bindings" +) + +// Rename an existing container. +func Rename(ctx context.Context, nameOrID string, options *RenameOptions) error { + if options == nil { + options = new(RenameOptions) + } + conn, err := bindings.GetClient(ctx) + if err != nil { + return err + } + params, err := options.ToParams() + if err != nil { + return err + } + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/rename", params, nil, nameOrID) + if err != nil { + return err + } + return response.Process(nil) +} diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go index 24402e982..10f553e52 100644 --- a/pkg/bindings/containers/types.go +++ b/pkg/bindings/containers/types.go @@ -194,6 +194,13 @@ type InitOptions struct{} // ShouldRestartOptions type ShouldRestartOptions struct{} +//go:generate go run ../generator/generator.go RenameOptions +// RenameOptions are options for renaming containers. +// The Name field is required. +type RenameOptions struct { + Name *string +} + //go:generate go run ../generator/generator.go ResizeTTYOptions // ResizeTTYOptions are optional options for resizing // container TTYs diff --git a/pkg/bindings/containers/types_rename_options.go b/pkg/bindings/containers/types_rename_options.go new file mode 100644 index 000000000..b7a723f7a --- /dev/null +++ b/pkg/bindings/containers/types_rename_options.go @@ -0,0 +1,104 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + "strings" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. +*/ + +// Changed +func (o *RenameOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *RenameOptions) 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 + } + fieldName = strings.ToLower(fieldName) + 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.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.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) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithName +func (o *RenameOptions) WithName(value string) *RenameOptions { + v := &value + o.Name = v + return o +} + +// GetName +func (o *RenameOptions) GetName() string { + var name string + if o.Name == nil { + return name + } + return *o.Name +} diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go index d34ab87d9..02765816f 100644 --- a/pkg/bindings/images/build.go +++ b/pkg/bindings/images/build.go @@ -2,6 +2,7 @@ package images import ( "archive/tar" + "compress/gzip" "context" "encoding/json" "io" @@ -18,6 +19,7 @@ import ( "github.com/containers/podman/v2/pkg/auth" "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/storage/pkg/fileutils" "github.com/docker/go-units" "github.com/hashicorp/go-multierror" jsoniter "github.com/json-iterator/go" @@ -138,12 +140,38 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO entries := make([]string, len(containerFiles)) copy(entries, containerFiles) entries = append(entries, options.ContextDirectory) - tarfile, err := nTar(entries...) + + excludes := options.Excludes + if len(excludes) == 0 { + excludes, err = parseDockerignore(options.ContextDirectory) + if err != nil { + return nil, err + } + } + + tarfile, err := nTar(excludes, entries...) if err != nil { + logrus.Errorf("cannot tar container entries %v error: %v", entries, err) return nil, err } defer tarfile.Close() - params.Set("dockerfile", filepath.Base(containerFiles[0])) + + containerFile, err := filepath.Abs(entries[0]) + if err != nil { + logrus.Errorf("cannot find absolute path of %v: %v", entries[0], err) + return nil, err + } + contextDir, err := filepath.Abs(entries[1]) + if err != nil { + logrus.Errorf("cannot find absolute path of %v: %v", entries[1], err) + return nil, err + } + + if strings.HasPrefix(containerFile, contextDir+string(filepath.Separator)) { + containerFile = strings.TrimPrefix(containerFile, contextDir+string(filepath.Separator)) + } + + params.Set("dockerfile", containerFile) conn, err := bindings.GetClient(ctx) if err != nil { @@ -200,52 +228,116 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO } } -func nTar(sources ...string) (io.ReadCloser, error) { +func nTar(excludes []string, sources ...string) (io.ReadCloser, error) { + pm, err := fileutils.NewPatternMatcher(excludes) + if err != nil { + return nil, errors.Wrapf(err, "error processing excludes list %v", excludes) + } + if len(sources) == 0 { return nil, errors.New("No source(s) provided for build") } pr, pw := io.Pipe() - tw := tar.NewWriter(pw) + gw := gzip.NewWriter(pw) + tw := tar.NewWriter(gw) var merr error go func() { defer pw.Close() + defer gw.Close() defer tw.Close() for _, src := range sources { - s := src - err := filepath.Walk(s, func(path string, info os.FileInfo, err error) error { + s, err := filepath.Abs(src) + if err != nil { + logrus.Errorf("cannot stat one of source context: %v", err) + merr = multierror.Append(merr, err) + return + } + + err = filepath.Walk(s, func(path string, info os.FileInfo, err error) error { if err != nil { return err } - if !info.Mode().IsRegular() || path == s { - return nil - } - f, lerr := os.Open(path) - if lerr != nil { - return lerr + if path == s { + return nil // skip root dir } name := strings.TrimPrefix(path, s+string(filepath.Separator)) - hdr, lerr := tar.FileInfoHeader(info, name) - if lerr != nil { - f.Close() - return lerr + + excluded, err := pm.Matches(filepath.ToSlash(name)) // nolint:staticcheck + if err != nil { + return errors.Wrapf(err, "error checking if %q is excluded", name) } - hdr.Name = name - if lerr := tw.WriteHeader(hdr); lerr != nil { - f.Close() - return lerr + if excluded { + return nil } - _, cerr := io.Copy(tw, f) - f.Close() - return cerr + if info.Mode().IsRegular() { // add file item + f, lerr := os.Open(path) + if lerr != nil { + return lerr + } + + hdr, lerr := tar.FileInfoHeader(info, name) + if lerr != nil { + f.Close() + return lerr + } + hdr.Name = name + if lerr := tw.WriteHeader(hdr); lerr != nil { + f.Close() + return lerr + } + + _, cerr := io.Copy(tw, f) + f.Close() + return cerr + } else if info.Mode().IsDir() { // add folders + hdr, lerr := tar.FileInfoHeader(info, name) + if lerr != nil { + return lerr + } + hdr.Name = name + if lerr := tw.WriteHeader(hdr); lerr != nil { + return lerr + } + } else if info.Mode()&os.ModeSymlink != 0 { // add symlinks as it, not content + link, err := os.Readlink(path) + if err != nil { + return err + } + hdr, lerr := tar.FileInfoHeader(info, link) + if lerr != nil { + return lerr + } + hdr.Name = name + if lerr := tw.WriteHeader(hdr); lerr != nil { + return lerr + } + } //skip other than file,folder and symlinks + return nil }) merr = multierror.Append(merr, err) } }() return pr, merr } + +func parseDockerignore(root string) ([]string, error) { + ignore, err := ioutil.ReadFile(filepath.Join(root, ".dockerignore")) + if err != nil && !os.IsNotExist(err) { + return nil, errors.Wrapf(err, "error reading .dockerignore: '%s'", root) + } + rawexcludes := strings.Split(string(ignore), "\n") + excludes := make([]string, 0, len(rawexcludes)) + for _, e := range rawexcludes { + if len(e) == 0 || e[0] == '#' { + continue + } + excludes = append(excludes, e) + } + return excludes, nil +} diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 8aab4a9cd..4ee623703 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -823,5 +823,5 @@ func (ic *ContainerEngine) ShouldRestart(_ context.Context, id string) (bool, er // ContainerRename renames the given container. func (ic *ContainerEngine) ContainerRename(ctx context.Context, nameOrID string, opts entities.ContainerRenameOptions) error { - return errors.Errorf("NOT YET IMPLEMENTED") + return containers.Rename(ic.ClientCtx, nameOrID, new(containers.RenameOptions).WithName(opts.NewName)) } diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index 7dc32a314..e62131244 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -138,10 +138,23 @@ func makeCommand(ctx context.Context, s *specgen.SpecGenerator, img *image.Image return finalCommand, nil } +// canMountSys is a best-effort heuristic to detect whether mounting a new sysfs is permitted in the container +func canMountSys(isRootless, isNewUserns bool, s *specgen.SpecGenerator) bool { + if s.NetNS.IsHost() && (isRootless || isNewUserns) { + return false + } + if isNewUserns { + switch s.NetNS.NSMode { + case specgen.Slirp, specgen.Private, specgen.NoNetwork, specgen.Bridge: + return true + default: + return false + } + } + return true +} + func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, newImage *image.Image, mounts []spec.Mount, pod *libpod.Pod, finalCmd []string) (*spec.Spec, error) { - var ( - inUserNS bool - ) cgroupPerm := "ro" g, err := generate.New("linux") if err != nil { @@ -151,23 +164,11 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt g.RemoveMount("/dev/shm") g.HostSpecific = true addCgroup := true - canMountSys := true isRootless := rootless.IsRootless() - if isRootless { - inUserNS = true - } - if !s.UserNS.IsHost() { - if s.UserNS.IsContainer() || s.UserNS.IsPath() { - inUserNS = true - } - if s.UserNS.IsPrivate() { - inUserNS = true - } - } - if inUserNS && s.NetNS.NSMode != specgen.NoNetwork { - canMountSys = false - } + isNewUserns := s.UserNS.IsContainer() || s.UserNS.IsPath() || s.UserNS.IsPrivate() + + canMountSys := canMountSys(isRootless, isNewUserns, s) if s.Privileged && canMountSys { cgroupPerm = "rw" @@ -232,6 +233,8 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt g.AddMount(devPts) } + inUserNS := isRootless || isNewUserns + if inUserNS && s.IpcNS.IsHost() { g.RemoveMount("/dev/mqueue") devMqueue := spec.Mount{ diff --git a/test/e2e/build_test.go b/test/e2e/build_test.go index 0c74bf972..71b4c0089 100644 --- a/test/e2e/build_test.go +++ b/test/e2e/build_test.go @@ -234,7 +234,7 @@ RUN printenv http_proxy` }) It("podman build and check identity", func() { - session := podmanTest.Podman([]string{"build", "-f", "Containerfile.path", "--no-cache", "-t", "test", "build/basicalpine"}) + session := podmanTest.Podman([]string{"build", "-f", "build/basicalpine/Containerfile.path", "--no-cache", "-t", "test", "build/basicalpine"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -244,4 +244,218 @@ RUN printenv http_proxy` data := inspect.OutputToString() Expect(data).To(ContainSubstring(buildah.Version)) }) + + It("podman remote test container/docker file is not inside context dir", func() { + // Given + // Switch to temp dir and restore it afterwards + cwd, err := os.Getwd() + Expect(err).To(BeNil()) + + podmanTest.AddImageToRWStore(ALPINE) + + // Write target and fake files + targetPath, err := CreateTempDirInTempDir() + Expect(err).To(BeNil()) + targetSubPath := filepath.Join(targetPath, "subdir") + err = os.Mkdir(targetSubPath, 0755) + Expect(err).To(BeNil()) + dummyFile := filepath.Join(targetSubPath, "dummy") + err = ioutil.WriteFile(dummyFile, []byte("dummy"), 0644) + Expect(err).To(BeNil()) + + containerfile := `FROM quay.io/libpod/alpine:latest +ADD . /test +RUN find /test` + + containerfilePath := filepath.Join(targetPath, "Containerfile") + err = ioutil.WriteFile(containerfilePath, []byte(containerfile), 0644) + Expect(err).To(BeNil()) + + defer func() { + Expect(os.Chdir(cwd)).To(BeNil()) + Expect(os.RemoveAll(targetPath)).To(BeNil()) + }() + + // make cwd as context root path + Expect(os.Chdir(targetPath)).To(BeNil()) + + session := podmanTest.Podman([]string{"build", "-t", "test", "-f", "Containerfile", targetSubPath}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + ok, _ := session.GrepString("/test/dummy") + Expect(ok).To(BeTrue()) + }) + + It("podman remote test container/docker file is not at root of context dir", func() { + if IsRemote() { + podmanTest.StopRemoteService() + podmanTest.StartRemoteService() + } else { + Skip("Only valid at remote test") + } + // Given + // Switch to temp dir and restore it afterwards + cwd, err := os.Getwd() + Expect(err).To(BeNil()) + + podmanTest.AddImageToRWStore(ALPINE) + + // Write target and fake files + targetPath, err := CreateTempDirInTempDir() + Expect(err).To(BeNil()) + targetSubPath := filepath.Join(targetPath, "subdir") + err = os.Mkdir(targetSubPath, 0755) + Expect(err).To(BeNil()) + + containerfile := `FROM quay.io/libpod/alpine:latest` + + containerfilePath := filepath.Join(targetSubPath, "Containerfile") + err = ioutil.WriteFile(containerfilePath, []byte(containerfile), 0644) + Expect(err).To(BeNil()) + + defer func() { + Expect(os.Chdir(cwd)).To(BeNil()) + Expect(os.RemoveAll(targetPath)).To(BeNil()) + }() + + // make cwd as context root path + Expect(os.Chdir(targetPath)).To(BeNil()) + + session := podmanTest.Podman([]string{"build", "-t", "test", "-f", "subdir/Containerfile", "."}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + + It("podman remote test .dockerignore", func() { + if IsRemote() { + podmanTest.StopRemoteService() + podmanTest.StartRemoteService() + } else { + Skip("Only valid at remote test") + } + // Given + // Switch to temp dir and restore it afterwards + cwd, err := os.Getwd() + Expect(err).To(BeNil()) + + podmanTest.AddImageToRWStore(ALPINE) + + // Write target and fake files + targetPath, err := CreateTempDirInTempDir() + Expect(err).To(BeNil()) + + containerfile := `FROM quay.io/libpod/alpine:latest +ADD . /testfilter/ +RUN find /testfilter/` + + containerfilePath := filepath.Join(targetPath, "Containerfile") + err = ioutil.WriteFile(containerfilePath, []byte(containerfile), 0644) + Expect(err).To(BeNil()) + + targetSubPath := filepath.Join(targetPath, "subdir") + err = os.Mkdir(targetSubPath, 0755) + Expect(err).To(BeNil()) + + dummyFile1 := filepath.Join(targetPath, "dummy1") + err = ioutil.WriteFile(dummyFile1, []byte("dummy1"), 0644) + Expect(err).To(BeNil()) + + dummyFile2 := filepath.Join(targetPath, "dummy2") + err = ioutil.WriteFile(dummyFile2, []byte("dummy2"), 0644) + Expect(err).To(BeNil()) + + dummyFile3 := filepath.Join(targetSubPath, "dummy3") + err = ioutil.WriteFile(dummyFile3, []byte("dummy3"), 0644) + Expect(err).To(BeNil()) + + defer func() { + Expect(os.Chdir(cwd)).To(BeNil()) + Expect(os.RemoveAll(targetPath)).To(BeNil()) + }() + + // make cwd as context root path + Expect(os.Chdir(targetPath)).To(BeNil()) + + dockerignoreContent := `dummy1 +subdir**` + dockerignoreFile := filepath.Join(targetPath, ".dockerignore") + + // test .dockerignore + By("Test .dockererignore") + err = ioutil.WriteFile(dockerignoreFile, []byte(dockerignoreContent), 0644) + Expect(err).To(BeNil()) + + session := podmanTest.Podman([]string{"build", "-t", "test", "."}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + ok, _ := session.GrepString("/testfilter/dummy1") + Expect(ok).NotTo(BeTrue()) + ok, _ = session.GrepString("/testfilter/dummy2") + Expect(ok).To(BeTrue()) + ok, _ = session.GrepString("/testfilter/subdir") + Expect(ok).NotTo(BeTrue()) //.dockerignore filters both subdir and inside subdir + ok, _ = session.GrepString("/testfilter/subdir/dummy3") + Expect(ok).NotTo(BeTrue()) + }) + + It("podman remote test context dir contains empty dirs and symlinks", func() { + if IsRemote() { + podmanTest.StopRemoteService() + podmanTest.StartRemoteService() + } else { + Skip("Only valid at remote test") + } + // Given + // Switch to temp dir and restore it afterwards + cwd, err := os.Getwd() + Expect(err).To(BeNil()) + + podmanTest.AddImageToRWStore(ALPINE) + + // Write target and fake files + targetPath, err := CreateTempDirInTempDir() + Expect(err).To(BeNil()) + targetSubPath := filepath.Join(targetPath, "subdir") + err = os.Mkdir(targetSubPath, 0755) + Expect(err).To(BeNil()) + dummyFile := filepath.Join(targetSubPath, "dummy") + err = ioutil.WriteFile(dummyFile, []byte("dummy"), 0644) + Expect(err).To(BeNil()) + + emptyDir := filepath.Join(targetSubPath, "emptyDir") + err = os.Mkdir(emptyDir, 0755) + Expect(err).To(BeNil()) + Expect(os.Chdir(targetSubPath)).To(BeNil()) + Expect(os.Symlink("dummy", "dummy-symlink")).To(BeNil()) + + containerfile := `FROM quay.io/libpod/alpine:latest +ADD . /test +RUN find /test +RUN [[ -L /test/dummy-symlink ]] && echo SYMLNKOK || echo SYMLNKERR` + + containerfilePath := filepath.Join(targetSubPath, "Containerfile") + err = ioutil.WriteFile(containerfilePath, []byte(containerfile), 0644) + Expect(err).To(BeNil()) + + defer func() { + Expect(os.Chdir(cwd)).To(BeNil()) + Expect(os.RemoveAll(targetPath)).To(BeNil()) + }() + + // make cwd as context root path + Expect(os.Chdir(targetPath)).To(BeNil()) + + session := podmanTest.Podman([]string{"build", "-t", "test", targetSubPath}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + ok, _ := session.GrepString("/test/dummy") + Expect(ok).To(BeTrue()) + ok, _ = session.GrepString("/test/emptyDir") + Expect(ok).To(BeTrue()) + ok, _ = session.GrepString("/test/dummy-symlink") + Expect(ok).To(BeTrue()) + ok, _ = session.GrepString("SYMLNKOK") + Expect(ok).To(BeTrue()) + }) + }) diff --git a/test/e2e/pod_stats_test.go b/test/e2e/pod_stats_test.go index 287830102..1709b4f81 100644 --- a/test/e2e/pod_stats_test.go +++ b/test/e2e/pod_stats_test.go @@ -17,7 +17,7 @@ var _ = Describe("Podman pod stats", func() { ) BeforeEach(func() { - SkipIfRootless("Tests fail with both CGv1/2 + required --cgroup-manager=cgroupfs") + SkipIfRootlessCgroupsV1("Tests fail with both CGv1 + required --cgroup-manager=cgroupfs") if isContainerized() { SkipIfCgroupV1("All tests fail Error: unable to load cgroup at ...: cgroup deleted") } @@ -176,8 +176,7 @@ var _ = Describe("Podman pod stats", func() { }) It("podman stats on net=host post", func() { - // --net=host not supported for rootless pods at present - SkipIfRootlessCgroupsV1("Pause stats not supported in cgroups v1") + SkipIfRootless("--net=host not supported for rootless pods at present") podName := "testPod" podCreate := podmanTest.Podman([]string{"pod", "create", "--net=host", "--name", podName}) podCreate.WaitWithDefaultTimeout() diff --git a/test/e2e/rename_test.go b/test/e2e/rename_test.go index 324e6a54a..7affbaf56 100644 --- a/test/e2e/rename_test.go +++ b/test/e2e/rename_test.go @@ -17,7 +17,6 @@ var _ = Describe("podman rename", func() { ) BeforeEach(func() { - SkipIfRemote("Rename not yet implemented by podman-remote") tempdir, err = CreateTempDirInTempDir() if err != nil { os.Exit(1) diff --git a/test/e2e/run_ns_test.go b/test/e2e/run_ns_test.go index 51657cb1e..29d2d4395 100644 --- a/test/e2e/run_ns_test.go +++ b/test/e2e/run_ns_test.go @@ -105,6 +105,14 @@ var _ = Describe("Podman run ns", func() { Expect(session).To(ExitWithError()) }) + It("podman run mounts fresh cgroup", func() { + session := podmanTest.Podman([]string{"run", fedoraMinimal, "grep", "cgroup", "/proc/self/mountinfo"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + output := session.OutputToString() + Expect(output).ToNot(ContainSubstring("..")) + }) + It("podman run --ipc=host --pid=host", func() { SkipIfRootlessCgroupsV1("Not supported for rootless + CGroupsV1") cmd := exec.Command("ls", "-l", "/proc/self/ns/pid") |