diff options
Diffstat (limited to 'pkg/domain/infra/abi')
-rw-r--r-- | pkg/domain/infra/abi/archive.go | 172 | ||||
-rw-r--r-- | pkg/domain/infra/abi/containers.go | 34 | ||||
-rw-r--r-- | pkg/domain/infra/abi/containers_stat.go | 251 | ||||
-rw-r--r-- | pkg/domain/infra/abi/cp.go | 70 | ||||
-rw-r--r-- | pkg/domain/infra/abi/images.go | 85 | ||||
-rw-r--r-- | pkg/domain/infra/abi/manifest.go | 17 | ||||
-rw-r--r-- | pkg/domain/infra/abi/parse/parse.go | 6 | ||||
-rw-r--r-- | pkg/domain/infra/abi/play.go | 18 | ||||
-rw-r--r-- | pkg/domain/infra/abi/pods.go | 4 | ||||
-rw-r--r-- | pkg/domain/infra/abi/system.go | 49 | ||||
-rw-r--r-- | pkg/domain/infra/abi/volumes.go | 22 |
11 files changed, 558 insertions, 170 deletions
diff --git a/pkg/domain/infra/abi/archive.go b/pkg/domain/infra/abi/archive.go new file mode 100644 index 000000000..809813756 --- /dev/null +++ b/pkg/domain/infra/abi/archive.go @@ -0,0 +1,172 @@ +package abi + +import ( + "context" + "io" + "strings" + + buildahCopiah "github.com/containers/buildah/copier" + "github.com/containers/buildah/pkg/chrootuser" + "github.com/containers/buildah/util" + "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/storage" + "github.com/containers/storage/pkg/archive" + "github.com/containers/storage/pkg/idtools" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// NOTE: Only the parent directory of the container path must exist. The path +// itself may be created while copying. +func (ic *ContainerEngine) ContainerCopyFromArchive(ctx context.Context, nameOrID string, containerPath string, reader io.Reader) (entities.ContainerCopyFunc, error) { + container, err := ic.Libpod.LookupContainer(nameOrID) + if err != nil { + return nil, err + } + + unmount := func() { + if err := container.Unmount(false); err != nil { + logrus.Errorf("Error unmounting container: %v", err) + } + } + + _, resolvedRoot, resolvedContainerPath, err := ic.containerStat(container, containerPath) + if err != nil { + unmount() + return nil, err + } + + decompressed, err := archive.DecompressStream(reader) + if err != nil { + unmount() + return nil, err + } + + idMappings, idPair, err := getIDMappingsAndPair(container, resolvedRoot) + if err != nil { + unmount() + return nil, err + } + + logrus.Debugf("Container copy *to* %q (resolved: %q) on container %q (ID: %s)", containerPath, resolvedContainerPath, container.Name(), container.ID()) + + return func() error { + defer unmount() + defer decompressed.Close() + putOptions := buildahCopiah.PutOptions{ + UIDMap: idMappings.UIDMap, + GIDMap: idMappings.GIDMap, + ChownDirs: idPair, + ChownFiles: idPair, + } + return buildahCopiah.Put(resolvedRoot, resolvedContainerPath, putOptions, decompressed) + }, nil +} + +func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID string, containerPath string, writer io.Writer) (entities.ContainerCopyFunc, error) { + container, err := ic.Libpod.LookupContainer(nameOrID) + if err != nil { + return nil, err + } + + unmount := func() { + if err := container.Unmount(false); err != nil { + logrus.Errorf("Error unmounting container: %v", err) + } + } + + // Make sure that "/" copies the *contents* of the mount point and not + // the directory. + if containerPath == "/" { + containerPath = "/." + } + + _, resolvedRoot, resolvedContainerPath, err := ic.containerStat(container, containerPath) + if err != nil { + unmount() + return nil, err + } + + idMappings, idPair, err := getIDMappingsAndPair(container, resolvedRoot) + if err != nil { + unmount() + return nil, err + } + + logrus.Debugf("Container copy *from* %q (resolved: %q) on container %q (ID: %s)", containerPath, resolvedContainerPath, container.Name(), container.ID()) + + return func() error { + defer container.Unmount(false) + getOptions := buildahCopiah.GetOptions{ + // Unless the specified path ends with ".", we want to copy the base directory. + KeepDirectoryNames: !strings.HasSuffix(resolvedContainerPath, "."), + UIDMap: idMappings.UIDMap, + GIDMap: idMappings.GIDMap, + ChownDirs: idPair, + ChownFiles: idPair, + } + return buildahCopiah.Get(resolvedRoot, "", getOptions, []string{resolvedContainerPath}, writer) + }, nil +} + +// getIDMappingsAndPair returns the ID mappings for the container and the host +// ID pair. +func getIDMappingsAndPair(container *libpod.Container, containerMount string) (*storage.IDMappingOptions, *idtools.IDPair, error) { + user, err := getContainerUser(container, containerMount) + if err != nil { + return nil, nil, err + } + + idMappingOpts, err := container.IDMappings() + if err != nil { + return nil, nil, err + } + + hostUID, hostGID, err := util.GetHostIDs(idtoolsToRuntimeSpec(idMappingOpts.UIDMap), idtoolsToRuntimeSpec(idMappingOpts.GIDMap), user.UID, user.GID) + if err != nil { + return nil, nil, err + } + + idPair := idtools.IDPair{UID: int(hostUID), GID: int(hostGID)} + return &idMappingOpts, &idPair, nil +} + +// getContainerUser returns the specs.User of the container. +func getContainerUser(container *libpod.Container, mountPoint string) (specs.User, error) { + userspec := container.Config().User + + uid, gid, _, err := chrootuser.GetUser(mountPoint, userspec) + u := specs.User{ + UID: uid, + GID: gid, + Username: userspec, + } + + if !strings.Contains(userspec, ":") { + groups, err2 := chrootuser.GetAdditionalGroupsForUser(mountPoint, uint64(u.UID)) + if err2 != nil { + if errors.Cause(err2) != chrootuser.ErrNoSuchUser && err == nil { + err = err2 + } + } else { + u.AdditionalGids = groups + } + } + + return u, err +} + +// idtoolsToRuntimeSpec converts idtools ID mapping to the one of the runtime spec. +func idtoolsToRuntimeSpec(idMaps []idtools.IDMap) (convertedIDMap []specs.LinuxIDMapping) { + for _, idmap := range idMaps { + tempIDMap := specs.LinuxIDMapping{ + ContainerID: uint32(idmap.ContainerID), + HostID: uint32(idmap.HostID), + Size: uint32(idmap.Size), + } + convertedIDMap = append(convertedIDMap, tempIDMap) + } + return convertedIDMap +} diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index ff4277a2e..9d7c2daea 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -16,12 +16,13 @@ import ( "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/events" - lpfilters "github.com/containers/podman/v2/libpod/filters" "github.com/containers/podman/v2/libpod/image" "github.com/containers/podman/v2/libpod/logs" "github.com/containers/podman/v2/pkg/cgroups" "github.com/containers/podman/v2/pkg/checkpoint" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" + dfilters "github.com/containers/podman/v2/pkg/domain/filters" "github.com/containers/podman/v2/pkg/domain/infra/abi/terminal" parallelctr "github.com/containers/podman/v2/pkg/parallel/ctr" "github.com/containers/podman/v2/pkg/ps" @@ -204,31 +205,27 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin return reports, nil } -func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities.ContainerPruneOptions) (*entities.ContainerPruneReport, error) { +func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities.ContainerPruneOptions) ([]*reports.PruneReport, error) { filterFuncs := make([]libpod.ContainerFilter, 0, len(options.Filters)) for k, v := range options.Filters { - generatedFunc, err := lpfilters.GenerateContainerFilterFuncs(k, v, ic.Libpod) + generatedFunc, err := dfilters.GenerateContainerFilterFuncs(k, v, ic.Libpod) if err != nil { return nil, err } filterFuncs = append(filterFuncs, generatedFunc) } - return ic.pruneContainersHelper(filterFuncs) -} - -func (ic *ContainerEngine) pruneContainersHelper(filterFuncs []libpod.ContainerFilter) (*entities.ContainerPruneReport, error) { - prunedContainers, pruneErrors, err := ic.Libpod.PruneContainers(filterFuncs) - if err != nil { - return nil, err - } - report := entities.ContainerPruneReport{ - ID: prunedContainers, - Err: pruneErrors, - } - return &report, nil + return ic.Libpod.PruneContainers(filterFuncs) } func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []string, options entities.KillOptions) ([]*entities.KillReport, error) { + for _, cidFile := range options.CIDFiles { + content, err := ioutil.ReadFile(cidFile) + if err != nil { + return nil, errors.Wrap(err, "error reading CIDFile") + } + id := strings.Split(string(content), "\n")[0] + namesOrIds = append(namesOrIds, id) + } sig, err := signal.ParseSignalNameOrNumber(options.Signal) if err != nil { return nil, err @@ -246,6 +243,7 @@ func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []strin } return reports, nil } + func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []string, options entities.RestartOptions) ([]*entities.RestartReport, error) { var ( ctrs []*libpod.Container @@ -925,7 +923,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 +961,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/abi/containers_stat.go b/pkg/domain/infra/abi/containers_stat.go new file mode 100644 index 000000000..5b43ee2f4 --- /dev/null +++ b/pkg/domain/infra/abi/containers_stat.go @@ -0,0 +1,251 @@ +package abi + +import ( + "context" + "os" + "path/filepath" + "strings" + + buildahCopiah "github.com/containers/buildah/copier" + "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/pkg/copy" + "github.com/containers/podman/v2/pkg/domain/entities" + securejoin "github.com/cyphar/filepath-securejoin" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +func (ic *ContainerEngine) containerStat(container *libpod.Container, containerPath string) (*entities.ContainerStatReport, string, string, error) { + containerMountPoint, err := container.Mount() + if err != nil { + return nil, "", "", err + } + + // Make sure that "/" copies the *contents* of the mount point and not + // the directory. + if containerPath == "/" { + containerPath += "/." + } + + // Now resolve the container's path. It may hit a volume, it may hit a + // bind mount, it may be relative. + resolvedRoot, resolvedContainerPath, err := resolveContainerPaths(container, containerMountPoint, containerPath) + if err != nil { + return nil, "", "", err + } + + statInfo, statInfoErr := secureStat(resolvedRoot, resolvedContainerPath) + if statInfoErr != nil { + // Not all errors from secureStat map to ErrNotExist, so we + // have to look into the error string. Turning it into an + // ENOENT let's the API handlers return the correct status code + // which is crucial for the remote client. + if os.IsNotExist(err) || strings.Contains(statInfoErr.Error(), "o such file or directory") { + statInfoErr = copy.ENOENT + } + // If statInfo is nil, there's nothing we can do anymore. A + // non-nil statInfo may indicate a symlink where we must have + // a closer look. + if statInfo == nil { + return nil, "", "", statInfoErr + } + } + + // Now make sure that the info's LinkTarget is relative to the + // container's mount. + var absContainerPath string + + if statInfo.IsSymlink { + // Evaluated symlinks are always relative to the container's mount point. + absContainerPath = statInfo.ImmediateTarget + } else if strings.HasPrefix(resolvedContainerPath, containerMountPoint) { + // If the path is on the container's mount point, strip it off. + absContainerPath = strings.TrimPrefix(resolvedContainerPath, containerMountPoint) + absContainerPath = filepath.Join("/", absContainerPath) + } else { + // No symlink and not on the container's mount point, so let's + // move it back to the original input. It must have evaluated + // to a volume or bind mount but we cannot return host paths. + absContainerPath = containerPath + } + + // Now we need to make sure to preserve the base path as specified by + // the user. The `filepath` packages likes to remove trailing slashes + // and dots that are crucial to the copy logic. + absContainerPath = copy.PreserveBasePath(containerPath, absContainerPath) + resolvedContainerPath = copy.PreserveBasePath(containerPath, resolvedContainerPath) + + info := copy.FileInfo{ + IsDir: statInfo.IsDir, + Name: filepath.Base(absContainerPath), + Size: statInfo.Size, + Mode: statInfo.Mode, + ModTime: statInfo.ModTime, + LinkTarget: absContainerPath, + } + + return &entities.ContainerStatReport{FileInfo: info}, resolvedRoot, resolvedContainerPath, statInfoErr +} + +func (ic *ContainerEngine) ContainerStat(ctx context.Context, nameOrID string, containerPath string) (*entities.ContainerStatReport, error) { + container, err := ic.Libpod.LookupContainer(nameOrID) + if err != nil { + return nil, err + } + + defer func() { + if err := container.Unmount(false); err != nil { + logrus.Errorf("Error unmounting container: %v", err) + } + }() + + statReport, _, _, err := ic.containerStat(container, containerPath) + return statReport, err +} + +// resolveContainerPaths resolves the container's mount point and the container +// path as specified by the user. Both may resolve to paths outside of the +// container's mount point when the container path hits a volume or bind mount. +// +// NOTE: We must take volumes and bind mounts into account as, regrettably, we +// can copy to/from stopped containers. In that case, the volumes and bind +// mounts are not present. For running containers, the runtime (e.g., runc or +// crun) takes care of these mounts. For stopped ones, we need to do quite +// some dance, as done below. +func resolveContainerPaths(container *libpod.Container, mountPoint string, containerPath string) (string, string, error) { + // Let's first make sure we have a path relative to the mount point. + pathRelativeToContainerMountPoint := containerPath + if !filepath.IsAbs(containerPath) { + // If the containerPath is not absolute, it's relative to the + // container's working dir. To be extra careful, let's first + // join the working dir with "/", and the add the containerPath + // to it. + pathRelativeToContainerMountPoint = filepath.Join(filepath.Join("/", container.WorkingDir()), containerPath) + } + resolvedPathOnTheContainerMountPoint := filepath.Join(mountPoint, pathRelativeToContainerMountPoint) + pathRelativeToContainerMountPoint = strings.TrimPrefix(pathRelativeToContainerMountPoint, mountPoint) + pathRelativeToContainerMountPoint = filepath.Join("/", pathRelativeToContainerMountPoint) + + // Now we have an "absolute container Path" but not yet resolved on the + // host (e.g., "/foo/bar/file.txt"). As mentioned above, we need to + // check if "/foo/bar/file.txt" is on a volume or bind mount. To do + // that, we need to walk *down* the paths to the root. Assuming + // volume-1 is mounted to "/foo" and volume-2 is mounted to "/foo/bar", + // we must select "/foo/bar". Once selected, we need to rebase the + // remainder (i.e, "/file.txt") on the volume's mount point on the + // host. Same applies to bind mounts. + + searchPath := pathRelativeToContainerMountPoint + for { + volume, err := findVolume(container, searchPath) + if err != nil { + return "", "", err + } + if volume != nil { + logrus.Debugf("Container path %q resolved to volume %q on path %q", containerPath, volume.Name(), searchPath) + // We found a matching volume for searchPath. We now + // need to first find the relative path of our input + // path to the searchPath, and then join it with the + // volume's mount point. + pathRelativeToVolume := strings.TrimPrefix(pathRelativeToContainerMountPoint, searchPath) + absolutePathOnTheVolumeMount, err := securejoin.SecureJoin(volume.MountPoint(), pathRelativeToVolume) + if err != nil { + return "", "", err + } + return volume.MountPoint(), absolutePathOnTheVolumeMount, nil + } + + if mount := findBindMount(container, searchPath); mount != nil { + logrus.Debugf("Container path %q resolved to bind mount %q:%q on path %q", containerPath, mount.Source, mount.Destination, searchPath) + // We found a matching bind mount for searchPath. We + // now need to first find the relative path of our + // input path to the searchPath, and then join it with + // the source of the bind mount. + pathRelativeToBindMount := strings.TrimPrefix(pathRelativeToContainerMountPoint, searchPath) + absolutePathOnTheBindMount, err := securejoin.SecureJoin(mount.Source, pathRelativeToBindMount) + if err != nil { + return "", "", err + } + return mount.Source, absolutePathOnTheBindMount, nil + + } + + if searchPath == "/" { + // Cannot go beyond "/", so we're done. + break + } + + // Walk *down* the path (e.g., "/foo/bar/x" -> "/foo/bar"). + searchPath = filepath.Dir(searchPath) + } + + // No volume, no bind mount but just a normal path on the container. + return mountPoint, resolvedPathOnTheContainerMountPoint, nil +} + +// findVolume checks if the specified container path matches a volume inside +// the container. It returns a matching volume or nil. +func findVolume(c *libpod.Container, containerPath string) (*libpod.Volume, error) { + runtime := c.Runtime() + cleanedContainerPath := filepath.Clean(containerPath) + for _, vol := range c.Config().NamedVolumes { + if cleanedContainerPath == filepath.Clean(vol.Dest) { + return runtime.GetVolume(vol.Name) + } + } + return nil, nil +} + +// findBindMount checks if the specified container path matches a bind mount +// inside the container. It returns a matching mount or nil. +func findBindMount(c *libpod.Container, containerPath string) *specs.Mount { + cleanedPath := filepath.Clean(containerPath) + for _, m := range c.Config().Spec.Mounts { + if m.Type != "bind" { + continue + } + if cleanedPath == filepath.Clean(m.Destination) { + mount := m + return &mount + } + } + return nil +} + +// secureStat extracts file info for path in a chroot'ed environment in root. +func secureStat(root string, path string) (*buildahCopiah.StatForItem, error) { + var glob string + var err error + + // If root and path are equal, then dir must be empty and the glob must + // be ".". + if filepath.Clean(root) == filepath.Clean(path) { + glob = "." + } else { + glob, err = filepath.Rel(root, path) + if err != nil { + return nil, err + } + } + + globStats, err := buildahCopiah.Stat(root, "", buildahCopiah.StatOptions{}, []string{glob}) + if err != nil { + return nil, err + } + + if len(globStats) != 1 { + return nil, errors.Errorf("internal error: secureStat: expected 1 item but got %d", len(globStats)) + } + + stat, exists := globStats[0].Results[glob] // only one glob passed, so that's okay + if !exists { + return nil, copy.ENOENT + } + + var statErr error + if stat.Error != "" { + statErr = errors.New(stat.Error) + } + return stat, statErr +} diff --git a/pkg/domain/infra/abi/cp.go b/pkg/domain/infra/abi/cp.go deleted file mode 100644 index 362053cce..000000000 --- a/pkg/domain/infra/abi/cp.go +++ /dev/null @@ -1,70 +0,0 @@ -package abi - -import ( - "context" - - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/pkg/copy" - "github.com/containers/podman/v2/pkg/domain/entities" -) - -func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) error { - // Parse user input. - sourceContainerStr, sourcePath, destContainerStr, destPath, err := copy.ParseSourceAndDestination(source, dest) - if err != nil { - return err - } - - // Look up containers. - var sourceContainer, destContainer *libpod.Container - if len(sourceContainerStr) > 0 { - sourceContainer, err = ic.Libpod.LookupContainer(sourceContainerStr) - if err != nil { - return err - } - } - if len(destContainerStr) > 0 { - destContainer, err = ic.Libpod.LookupContainer(destContainerStr) - if err != nil { - return err - } - } - - var sourceItem, destinationItem copy.CopyItem - - // Source ... container OR host. - if sourceContainer != nil { - sourceItem, err = copy.CopyItemForContainer(sourceContainer, sourcePath, options.Pause, true) - defer sourceItem.CleanUp() - if err != nil { - return err - } - } else { - sourceItem, err = copy.CopyItemForHost(sourcePath, true) - if err != nil { - return err - } - } - - // Destination ... container OR host. - if destContainer != nil { - destinationItem, err = copy.CopyItemForContainer(destContainer, destPath, options.Pause, false) - defer destinationItem.CleanUp() - if err != nil { - return err - } - } else { - destinationItem, err = copy.CopyItemForHost(destPath, false) - defer destinationItem.CleanUp() - if err != nil { - return err - } - } - - // Copy from the host to the container. - copier, err := copy.GetCopier(&sourceItem, &destinationItem, options.Extract) - if err != nil { - return err - } - return copier.Copy() -} diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 57a2bc4cf..19f081abb 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -24,10 +24,13 @@ import ( "github.com/containers/podman/v2/libpod/image" libpodImage "github.com/containers/podman/v2/libpod/image" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" domainUtils "github.com/containers/podman/v2/pkg/domain/utils" "github.com/containers/podman/v2/pkg/rootless" "github.com/containers/podman/v2/pkg/util" "github.com/containers/storage" + dockerRef "github.com/docker/distribution/reference" + "github.com/opencontainers/go-digest" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -47,19 +50,12 @@ func (ir *ImageEngine) Exists(_ context.Context, nameOrID string) (*entities.Boo return &entities.BoolReport{Value: err == nil}, nil } -func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) { - results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, opts.Filter) +func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) ([]*reports.PruneReport, error) { + reports, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, opts.Filter) if err != nil { return nil, err } - - report := entities.ImagePruneReport{ - Report: entities.Report{ - Id: results, - Err: nil, - }, - } - return &report, nil + return reports, err } func (ir *ImageEngine) History(ctx context.Context, nameOrID string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) { @@ -718,9 +714,9 @@ func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entitie logrus.Errorf("unable to close %s image source %q", srcRef.DockerReference().Name(), err) } }() - getManifest, _, err := rawSource.GetManifest(ctx, nil) + topManifestBlob, manifestType, err := rawSource.GetManifest(ctx, nil) if err != nil { - return errors.Wrapf(err, "error getting getManifest") + return errors.Wrapf(err, "error getting manifest blob") } dockerReference := rawSource.Reference().DockerReference() if dockerReference == nil { @@ -743,34 +739,34 @@ func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entitie return err } } - manifestDigest, err := manifest.Digest(getManifest) + manifestDigest, err := manifest.Digest(topManifestBlob) if err != nil { return err } - // create signature - newSig, err := signature.SignDockerManifest(getManifest, dockerReference.String(), mech, options.SignBy) - if err != nil { - return errors.Wrapf(err, "error creating new signature") - } - // create the signstore file - signatureDir := fmt.Sprintf("%s@%s=%s", sigStoreDir, manifestDigest.Algorithm(), manifestDigest.Hex()) - if err := os.MkdirAll(signatureDir, 0751); err != nil { - // The directory is allowed to exist - if !os.IsExist(err) { - logrus.Error(err) - return nil + if options.All { + if !manifest.MIMETypeIsMultiImage(manifestType) { + return errors.Errorf("%s is not a multi-architecture image (manifest type %s)", signimage, manifestType) + } + list, err := manifest.ListFromBlob(topManifestBlob, manifestType) + if err != nil { + return errors.Wrapf(err, "Error parsing manifest list %q", string(topManifestBlob)) + } + instanceDigests := list.Instances() + for _, instanceDigest := range instanceDigests { + digest := instanceDigest + man, _, err := rawSource.GetManifest(ctx, &digest) + if err != nil { + return err + } + if err = putSignature(man, mech, sigStoreDir, instanceDigest, dockerReference, options); err != nil { + return errors.Wrapf(err, "error storing signature for %s, %v", dockerReference.String(), instanceDigest) + } } - } - sigFilename, err := getSigFilename(signatureDir) - if err != nil { - logrus.Errorf("error creating sigstore file: %v", err) return nil } - err = ioutil.WriteFile(filepath.Join(signatureDir, sigFilename), newSig, 0644) - if err != nil { - logrus.Errorf("error storing signature for %s", rawSource.Reference().DockerReference().String()) - return nil + if err = putSignature(topManifestBlob, mech, sigStoreDir, manifestDigest, dockerReference, options); err != nil { + return errors.Wrapf(err, "error storing signature for %s, %v", dockerReference.String(), manifestDigest) } return nil }() @@ -806,3 +802,26 @@ func localPathFromURI(url *url.URL) (string, error) { } return url.Path, nil } + +// putSignature creates signature and saves it to the signstore file +func putSignature(manifestBlob []byte, mech signature.SigningMechanism, sigStoreDir string, instanceDigest digest.Digest, dockerReference dockerRef.Reference, options entities.SignOptions) error { + newSig, err := signature.SignDockerManifest(manifestBlob, dockerReference.String(), mech, options.SignBy) + if err != nil { + return err + } + signatureDir := fmt.Sprintf("%s@%s=%s", sigStoreDir, instanceDigest.Algorithm(), instanceDigest.Hex()) + if err := os.MkdirAll(signatureDir, 0751); err != nil { + // The directory is allowed to exist + if !os.IsExist(err) { + return err + } + } + sigFilename, err := getSigFilename(signatureDir) + if err != nil { + return err + } + if err = ioutil.WriteFile(filepath.Join(signatureDir, sigFilename), newSig, 0644); err != nil { + return err + } + return nil +} diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go index 600d64b1d..0c734d10d 100644 --- a/pkg/domain/infra/abi/manifest.go +++ b/pkg/domain/infra/abi/manifest.go @@ -244,15 +244,16 @@ func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (stri } // ManifestPush pushes a manifest list or image index to the destination -func (ir *ImageEngine) ManifestPush(ctx context.Context, names []string, opts entities.ManifestPushOptions) error { - listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(names[0]) +func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination string, opts entities.ManifestPushOptions) error { + listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name) if err != nil { - return errors.Wrapf(err, "error retrieving local image from image name %s", names[0]) + return errors.Wrapf(err, "error retrieving local image from image name %s", name) } - dest, err := alltransports.ParseImageName(names[1]) + dest, err := alltransports.ParseImageName(destination) if err != nil { return err } + var manifestType string if opts.Format != "" { switch opts.Format { @@ -267,8 +268,8 @@ func (ir *ImageEngine) ManifestPush(ctx context.Context, names []string, opts en // Set the system context. sys := ir.Libpod.SystemContext() - if sys != nil { - sys = &types.SystemContext{} + if sys == nil { + sys = new(types.SystemContext) } sys.AuthFilePath = opts.Authfile sys.DockerInsecureSkipTLSVerify = opts.SkipTLSVerify @@ -296,12 +297,12 @@ func (ir *ImageEngine) ManifestPush(ctx context.Context, names []string, opts en if !opts.Quiet { options.ReportWriter = os.Stderr } - digest, err := listImage.PushManifest(dest, options) + manDigest, err := listImage.PushManifest(dest, options) if err == nil && opts.Purge { _, err = ir.Libpod.GetStore().DeleteImage(listImage.ID(), true) } if opts.DigestFile != "" { - if err = ioutil.WriteFile(opts.DigestFile, []byte(digest.String()), 0644); err != nil { + if err = ioutil.WriteFile(opts.DigestFile, []byte(manDigest.String()), 0644); err != nil { return buildahUtil.GetFailureCause(err, errors.Wrapf(err, "failed to write digest to file %q", opts.DigestFile)) } } diff --git a/pkg/domain/infra/abi/parse/parse.go b/pkg/domain/infra/abi/parse/parse.go index 37568ea11..6a6380e33 100644 --- a/pkg/domain/infra/abi/parse/parse.go +++ b/pkg/domain/infra/abi/parse/parse.go @@ -38,6 +38,9 @@ func VolumeOptions(opts map[string]string) ([]libpod.VolumeCreateOption, error) } logrus.Debugf("Removing uid= from options and adding WithVolumeUID for UID %d", intUID) libpodOptions = append(libpodOptions, libpod.WithVolumeUID(intUID)) + finalVal = append(finalVal, o) + // set option "UID": "$uid" + volumeOptions["UID"] = splitO[1] case "gid": if len(splitO) != 2 { return nil, errors.Wrapf(define.ErrInvalidArg, "gid option must provide a GID") @@ -48,6 +51,9 @@ func VolumeOptions(opts map[string]string) ([]libpod.VolumeCreateOption, error) } logrus.Debugf("Removing gid= from options and adding WithVolumeGID for GID %d", intGID) libpodOptions = append(libpodOptions, libpod.WithVolumeGID(intGID)) + finalVal = append(finalVal, o) + // set option "GID": "$gid" + volumeOptions["GID"] = splitO[1] default: finalVal = append(finalVal, o) } diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 3aeb6a2ee..cbc74a2f2 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -212,8 +212,10 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY return nil, errors.Wrapf(err, "Failed to parse image %q", container.Image) } // In kube, if the image is tagged with latest, it should always pull + // but if the domain is localhost, that means the image was built locally + // so do not attempt a pull. if tagged, isTagged := named.(reference.NamedTagged); isTagged { - if tagged.Tag() == image.LatestTag { + if tagged.Tag() == image.LatestTag && reference.Domain(named) != image.DefaultLocalRegistry { pullPolicy = util.PullImageAlways } } @@ -224,7 +226,19 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY return nil, err } - specGen, err := kube.ToSpecGen(ctx, container, container.Image, newImage, volumes, pod.ID(), podName, podInfraID, configMaps, seccompPaths, ctrRestartPolicy) + specgenOpts := kube.CtrSpecGenOptions{ + Container: container, + Image: newImage, + Volumes: volumes, + PodID: pod.ID(), + PodName: podName, + PodInfraID: podInfraID, + ConfigMaps: configMaps, + SeccompPaths: seccompPaths, + RestartPolicy: ctrRestartPolicy, + NetNSIsHost: p.NetNS.IsHost(), + } + specGen, err := kube.ToSpecGen(ctx, &specgenOpts) if err != nil { return nil, err } diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go index 11374e513..f108b770c 100644 --- a/pkg/domain/infra/abi/pods.go +++ b/pkg/domain/infra/abi/pods.go @@ -5,8 +5,8 @@ import ( "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" - lpfilters "github.com/containers/podman/v2/libpod/filters" "github.com/containers/podman/v2/pkg/domain/entities" + dfilters "github.com/containers/podman/v2/pkg/domain/filters" "github.com/containers/podman/v2/pkg/signal" "github.com/containers/podman/v2/pkg/specgen" "github.com/containers/podman/v2/pkg/specgen/generate" @@ -288,7 +288,7 @@ func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOpti filters := make([]libpod.PodFilter, 0, len(options.Filters)) for k, v := range options.Filters { - f, err := lpfilters.GeneratePodFilterFunc(k, v) + f, err := dfilters.GeneratePodFilterFunc(k, v) if err != nil { return nil, err } diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index 7ed58092b..67c018122 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io/ioutil" + "net/url" "os" "os/exec" "path/filepath" @@ -15,6 +16,7 @@ import ( "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/cgroups" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/containers/podman/v2/pkg/rootless" "github.com/containers/podman/v2/pkg/util" "github.com/containers/podman/v2/utils" @@ -168,6 +170,8 @@ func checkInput() error { // nolint:deadcode,unused // SystemPrune removes unused data from the system. Pruning pods, containers, volumes and images. func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.SystemPruneOptions) (*entities.SystemPruneReport, error) { var systemPruneReport = new(entities.SystemPruneReport) + var filters []string + reclaimedSpace := (uint64)(0) found := true for found { found = false @@ -179,51 +183,46 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys found = true } systemPruneReport.PodPruneReport = append(systemPruneReport.PodPruneReport, podPruneReport...) - containerPruneReport, err := ic.pruneContainersHelper(nil) + + // TODO: Figure out cleaner way to handle all of the different PruneOptions + containerPruneOptions := entities.ContainerPruneOptions{} + containerPruneOptions.Filters = (url.Values)(options.Filters) + + containerPruneReports, err := ic.ContainerPrune(ctx, containerPruneOptions) if err != nil { return nil, err } - if len(containerPruneReport.ID) > 0 { - found = true - } - if systemPruneReport.ContainerPruneReport == nil { - systemPruneReport.ContainerPruneReport = containerPruneReport - } else { - for name, val := range containerPruneReport.ID { - systemPruneReport.ContainerPruneReport.ID[name] = val - } + reclaimedSpace = reclaimedSpace + reports.PruneReportsSize(containerPruneReports) + systemPruneReport.ContainerPruneReports = append(systemPruneReport.ContainerPruneReports, containerPruneReports...) + for k, v := range options.Filters { + filters = append(filters, fmt.Sprintf("%s=%s", k, v[0])) } - - results, err := ic.Libpod.ImageRuntime().PruneImages(ctx, options.All, nil) + imagePruneReports, err := ic.Libpod.ImageRuntime().PruneImages(ctx, options.All, filters) + reclaimedSpace = reclaimedSpace + reports.PruneReportsSize(imagePruneReports) if err != nil { return nil, err } - if len(results) > 0 { + if len(imagePruneReports) > 0 { found = true } - if systemPruneReport.ImagePruneReport == nil { - systemPruneReport.ImagePruneReport = &entities.ImagePruneReport{ - Report: entities.Report{ - Id: results, - Err: nil, - }, - } - } else { - systemPruneReport.ImagePruneReport.Report.Id = append(systemPruneReport.ImagePruneReport.Report.Id, results...) - } + systemPruneReport.ImagePruneReports = append(systemPruneReport.ImagePruneReports, imagePruneReports...) if options.Volume { - volumePruneReport, err := ic.pruneVolumesHelper(ctx) + volumePruneOptions := entities.VolumePruneOptions{} + volumePruneOptions.Filters = (url.Values)(options.Filters) + volumePruneReport, err := ic.VolumePrune(ctx, volumePruneOptions) if err != nil { return nil, err } if len(volumePruneReport) > 0 { found = true } - systemPruneReport.VolumePruneReport = append(systemPruneReport.VolumePruneReport, volumePruneReport...) + reclaimedSpace = reclaimedSpace + reports.PruneReportsSize(volumePruneReport) + systemPruneReport.VolumePruneReports = append(systemPruneReport.VolumePruneReports, volumePruneReport...) } } + systemPruneReport.ReclaimedSpace = reclaimedSpace return systemPruneReport, nil } diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go index a7262f61b..3c9dd9fc0 100644 --- a/pkg/domain/infra/abi/volumes.go +++ b/pkg/domain/infra/abi/volumes.go @@ -6,6 +6,7 @@ import ( "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/containers/podman/v2/pkg/domain/filters" "github.com/containers/podman/v2/pkg/domain/infra/abi/parse" "github.com/pkg/errors" @@ -127,23 +128,20 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin return reports, errs, nil } -func (ic *ContainerEngine) VolumePrune(ctx context.Context) ([]*entities.VolumePruneReport, error) { - return ic.pruneVolumesHelper(ctx) +func (ic *ContainerEngine) VolumePrune(ctx context.Context, options entities.VolumePruneOptions) ([]*reports.PruneReport, error) { + filterFuncs, err := filters.GenerateVolumeFilters(options.Filters) + if err != nil { + return nil, err + } + return ic.pruneVolumesHelper(ctx, filterFuncs) } -func (ic *ContainerEngine) pruneVolumesHelper(ctx context.Context) ([]*entities.VolumePruneReport, error) { - pruned, err := ic.Libpod.PruneVolumes(ctx) +func (ic *ContainerEngine) pruneVolumesHelper(ctx context.Context, filterFuncs []libpod.VolumeFilter) ([]*reports.PruneReport, error) { + pruned, err := ic.Libpod.PruneVolumes(ctx, filterFuncs) if err != nil { return nil, err } - reports := make([]*entities.VolumePruneReport, 0, len(pruned)) - for k, v := range pruned { - reports = append(reports, &entities.VolumePruneReport{ - Err: v, - Id: k, - }) - } - return reports, nil + return pruned, nil } func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeListOptions) ([]*entities.VolumeListReport, error) { |