diff options
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/podman/cleanup.go | 2 | ||||
-rw-r--r-- | cmd/podman/cliconfig/config.go | 18 | ||||
-rw-r--r-- | cmd/podman/cliconfig/create.go | 4 | ||||
-rw-r--r-- | cmd/podman/commands.go | 2 | ||||
-rw-r--r-- | cmd/podman/containers_prune.go | 13 | ||||
-rw-r--r-- | cmd/podman/cp.go | 257 | ||||
-rw-r--r-- | cmd/podman/create.go | 3 | ||||
-rw-r--r-- | cmd/podman/image.go | 1 | ||||
-rw-r--r-- | cmd/podman/login.go | 23 | ||||
-rw-r--r-- | cmd/podman/main.go | 1 | ||||
-rw-r--r-- | cmd/podman/rm.go | 5 | ||||
-rw-r--r-- | cmd/podman/run.go | 2 | ||||
-rw-r--r-- | cmd/podman/start.go | 2 | ||||
-rw-r--r-- | cmd/podman/system_prune.go | 2 | ||||
-rw-r--r-- | cmd/podman/varlink/io.podman.varlink | 4 |
15 files changed, 313 insertions, 26 deletions
diff --git a/cmd/podman/cleanup.go b/cmd/podman/cleanup.go index 537679d75..e465a30e6 100644 --- a/cmd/podman/cleanup.go +++ b/cmd/podman/cleanup.go @@ -58,7 +58,7 @@ func cleanupCmd(c *cliconfig.CleanupValues) error { for _, ctr := range cleanupContainers { hadError := false if c.Remove { - if err := runtime.RemoveContainer(ctx, ctr, false); err != nil { + if err := runtime.RemoveContainer(ctx, ctr, false, false); err != nil { if lastError != nil { fmt.Fprintln(os.Stderr, lastError) } diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index 85ded6da0..f5d6a8685 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -135,6 +135,11 @@ type PruneImagesValues struct { All bool } +type PruneContainersValues struct { + PodmanCommand + Force bool +} + type ImportValues struct { PodmanCommand Change []string @@ -172,12 +177,13 @@ type LoadValues struct { type LoginValues struct { PodmanCommand - Password string - Username string - Authfile string - CertDir string - GetLogin bool - TlsVerify bool + Password string + StdinPassword bool + Username string + Authfile string + CertDir string + GetLogin bool + TlsVerify bool } type LogoutValues struct { diff --git a/cmd/podman/cliconfig/create.go b/cmd/podman/cliconfig/create.go index 68ba4d857..b5ca1be9c 100644 --- a/cmd/podman/cliconfig/create.go +++ b/cmd/podman/cliconfig/create.go @@ -20,3 +20,7 @@ type BuildValues struct { *buildahcli.NameSpaceResults *buildahcli.LayerResults } + +type CpValues struct { + PodmanCommand +} diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go index 9004a5941..fa3839a53 100644 --- a/cmd/podman/commands.go +++ b/cmd/podman/commands.go @@ -10,7 +10,6 @@ import ( func getMainCommands() []*cobra.Command { rootCommands := []*cobra.Command{ _attachCommand, - _buildCommand, _commitCommand, _createCommand, _diffCommand, @@ -54,7 +53,6 @@ func getMainCommands() []*cobra.Command { // Commands that the local client implements func getImageSubCommands() []*cobra.Command { return []*cobra.Command{ - _buildCommand, _loadCommand, _saveCommand, _signCommand, diff --git a/cmd/podman/containers_prune.go b/cmd/podman/containers_prune.go index acc138fe0..bae578e1d 100644 --- a/cmd/podman/containers_prune.go +++ b/cmd/podman/containers_prune.go @@ -13,13 +13,12 @@ import ( ) var ( - pruneContainersCommand cliconfig.ContainersPrune + pruneContainersCommand cliconfig.PruneContainersValues pruneContainersDescription = ` podman container prune Removes all exited containers ` - _pruneContainersCommand = &cobra.Command{ Use: "prune", Short: "Remove all stopped containers", @@ -35,9 +34,11 @@ var ( func init() { pruneContainersCommand.Command = _pruneContainersCommand pruneContainersCommand.SetUsageTemplate(UsageTemplate()) + flags := pruneContainersCommand.Flags() + flags.BoolVarP(&pruneContainersCommand.Force, "force", "f", false, "Force removal of a running container. The default is false") } -func pruneContainers(runtime *adapter.LocalRuntime, ctx context.Context, maxWorkers int, force bool) error { +func pruneContainers(runtime *adapter.LocalRuntime, ctx context.Context, maxWorkers int, force, volumes bool) error { var deleteFuncs []shared.ParallelWorkerInput filter := func(c *libpod.Container) bool { @@ -57,7 +58,7 @@ func pruneContainers(runtime *adapter.LocalRuntime, ctx context.Context, maxWork for _, container := range delContainers { con := container f := func() error { - return runtime.RemoveContainer(ctx, con, force) + return runtime.RemoveContainer(ctx, con, force, volumes) } deleteFuncs = append(deleteFuncs, shared.ParallelWorkerInput{ @@ -70,7 +71,7 @@ func pruneContainers(runtime *adapter.LocalRuntime, ctx context.Context, maxWork return printParallelOutput(deleteErrors, errCount) } -func pruneContainersCmd(c *cliconfig.ContainersPrune) error { +func pruneContainersCmd(c *cliconfig.PruneContainersValues) error { runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") @@ -83,5 +84,5 @@ func pruneContainersCmd(c *cliconfig.ContainersPrune) error { } logrus.Debugf("Setting maximum workers to %d", maxWorkers) - return pruneContainers(runtime, getContext(), maxWorkers, c.Bool("force")) + return pruneContainers(runtime, getContext(), maxWorkers, c.Bool("force"), c.Bool("volumes")) } diff --git a/cmd/podman/cp.go b/cmd/podman/cp.go new file mode 100644 index 000000000..89114fda1 --- /dev/null +++ b/cmd/podman/cp.go @@ -0,0 +1,257 @@ +package main + +import ( + "os" + "path/filepath" + "strings" + + "github.com/containers/buildah/util" + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/chrootuser" + "github.com/containers/storage" + "github.com/containers/storage/pkg/archive" + "github.com/containers/storage/pkg/chrootarchive" + "github.com/containers/storage/pkg/idtools" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var ( + cpCommand cliconfig.CpValues + + cpDescription = "Copy files/folders between a container and the local filesystem" + _cpCommand = &cobra.Command{ + Use: "cp", + Short: "Copy files/folders between a container and the local filesystem", + Long: cpDescription, + RunE: func(cmd *cobra.Command, args []string) error { + cpCommand.InputArgs = args + cpCommand.GlobalFlags = MainGlobalOpts + return cpCmd(&cpCommand) + }, + Example: "[CONTAINER:]SRC_PATH [CONTAINER:]DEST_PATH", + } +) + +func init() { + cpCommand.Command = _cpCommand + rootCmd.AddCommand(cpCommand.Command) +} + +func cpCmd(c *cliconfig.CpValues) error { + args := c.InputArgs + if len(args) != 2 { + return errors.Errorf("you must provide a source path and a destination path") + } + + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) + if err != nil { + return errors.Wrapf(err, "could not get runtime") + } + defer runtime.Shutdown(false) + + return copyBetweenHostAndContainer(runtime, args[0], args[1]) +} + +func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest string) error { + + srcCtr, srcPath := parsePath(runtime, src) + destCtr, destPath := parsePath(runtime, dest) + + if (srcCtr == nil && destCtr == nil) || (srcCtr != nil && destCtr != nil) { + return errors.Errorf("invalid arguments %s, %s you must use just one container", src, dest) + } + + if len(srcPath) == 0 || len(destPath) == 0 { + return errors.Errorf("invalid arguments %s, %s you must specify paths", src, dest) + } + ctr := srcCtr + isFromHostToCtr := (ctr == nil) + if isFromHostToCtr { + ctr = destCtr + } + + mountPoint, err := ctr.Mount() + if err != nil { + return err + } + defer ctr.Unmount(false) + user, err := getUser(mountPoint, ctr.User()) + if err != nil { + return err + } + idMappingOpts, err := ctr.IDMappings() + if err != nil { + return errors.Wrapf(err, "error getting IDMappingOptions") + } + containerOwner := idtools.IDPair{UID: int(user.UID), GID: int(user.GID)} + hostUID, hostGID, err := util.GetHostIDs(convertIDMap(idMappingOpts.UIDMap), convertIDMap(idMappingOpts.GIDMap), user.UID, user.GID) + if err != nil { + return err + } + + hostOwner := idtools.IDPair{UID: int(hostUID), GID: int(hostGID)} + + var glob []string + if isFromHostToCtr { + if filepath.IsAbs(destPath) { + destPath = filepath.Join(mountPoint, destPath) + + } else { + if err = idtools.MkdirAllAndChownNew(filepath.Join(mountPoint, ctr.WorkingDir()), 0755, hostOwner); err != nil { + return errors.Wrapf(err, "error creating directory %q", destPath) + } + destPath = filepath.Join(mountPoint, ctr.WorkingDir(), destPath) + } + } else { + if filepath.IsAbs(srcPath) { + srcPath = filepath.Join(mountPoint, srcPath) + } else { + srcPath = filepath.Join(mountPoint, ctr.WorkingDir(), srcPath) + } + } + glob, err = filepath.Glob(srcPath) + if err != nil { + return errors.Wrapf(err, "invalid glob %q", srcPath) + } + if len(glob) == 0 { + glob = append(glob, srcPath) + } + if !filepath.IsAbs(destPath) { + dir, err := os.Getwd() + if err != nil { + return errors.Wrapf(err, "err getting current working directory") + } + destPath = filepath.Join(dir, destPath) + } + + var lastError error + for _, src := range glob { + err := copy(src, destPath, dest, idMappingOpts, &containerOwner) + if lastError != nil { + logrus.Error(lastError) + } + lastError = err + } + return lastError +} + +func getUser(mountPoint string, userspec string) (specs.User, error) { + 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 +} + +func parsePath(runtime *libpod.Runtime, path string) (*libpod.Container, string) { + pathArr := strings.SplitN(path, ":", 2) + if len(pathArr) == 2 { + ctr, err := runtime.LookupContainer(pathArr[0]) + if err == nil { + return ctr, pathArr[1] + } + } + return nil, path +} + +func getPathInfo(path string) (string, os.FileInfo, error) { + path, err := filepath.EvalSymlinks(path) + if err != nil { + return "", nil, errors.Wrapf(err, "error evaluating symlinks %q", path) + } + srcfi, err := os.Stat(path) + if err != nil { + return "", nil, errors.Wrapf(err, "error reading path %q", path) + } + return path, srcfi, nil +} + +func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, chownOpts *idtools.IDPair) error { + srcPath, err := filepath.EvalSymlinks(src) + if err != nil { + return errors.Wrapf(err, "error evaluating symlinks %q", srcPath) + } + + srcPath, srcfi, err := getPathInfo(srcPath) + if err != nil { + return err + } + destdir := destPath + if !srcfi.IsDir() && !strings.HasSuffix(dest, string(os.PathSeparator)) { + destdir = filepath.Dir(destPath) + } + if err = os.MkdirAll(destdir, 0755); err != nil { + return errors.Wrapf(err, "error creating directory %q", destdir) + } + + // return functions for copying items + copyFileWithTar := chrootarchive.CopyFileWithTarAndChown(chownOpts, digest.Canonical.Digester().Hash(), idMappingOpts.UIDMap, idMappingOpts.GIDMap) + copyWithTar := chrootarchive.CopyWithTarAndChown(chownOpts, digest.Canonical.Digester().Hash(), idMappingOpts.UIDMap, idMappingOpts.GIDMap) + untarPath := chrootarchive.UntarPathAndChown(chownOpts, digest.Canonical.Digester().Hash(), idMappingOpts.UIDMap, idMappingOpts.GIDMap) + + if srcfi.IsDir() { + + logrus.Debugf("copying %q to %q", srcPath+string(os.PathSeparator)+"*", dest+string(os.PathSeparator)+"*") + if err = copyWithTar(srcPath, destPath); err != nil { + return errors.Wrapf(err, "error copying %q to %q", srcPath, dest) + } + return nil + } + if !archive.IsArchivePath(srcPath) { + // This srcPath is a file, and either it's not an + // archive, or we don't care whether or not it's an + // archive. + destfi, err := os.Stat(destPath) + if err != nil { + if !os.IsNotExist(err) { + return errors.Wrapf(err, "failed to get stat of dest path %s", destPath) + } + } + if destfi != nil && destfi.IsDir() { + destPath = filepath.Join(destPath, filepath.Base(srcPath)) + } + // Copy the file, preserving attributes. + logrus.Debugf("copying %q to %q", srcPath, destPath) + if err = copyFileWithTar(srcPath, destPath); err != nil { + return errors.Wrapf(err, "error copying %q to %q", srcPath, destPath) + } + return nil + } + // We're extracting an archive into the destination directory. + logrus.Debugf("extracting contents of %q into %q", srcPath, destPath) + if err = untarPath(srcPath, destPath); err != nil { + return errors.Wrapf(err, "error extracting %q into %q", srcPath, destPath) + } + return nil +} + +func convertIDMap(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/cmd/podman/create.go b/cmd/podman/create.go index 1a7f419c0..392163424 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -646,9 +646,10 @@ func parseCreateOpts(ctx context.Context, c *cliconfig.PodmanCommand, runtime *l } var ImageVolumes map[string]struct{} - if data != nil { + if data != nil && c.String("image-volume") != "ignore" { ImageVolumes = data.Config.Volumes } + var imageVolType = map[string]string{ "bind": "", "tmpfs": "", diff --git a/cmd/podman/image.go b/cmd/podman/image.go index edc37b28a..4f9c7cd6a 100644 --- a/cmd/podman/image.go +++ b/cmd/podman/image.go @@ -18,6 +18,7 @@ var ( //imageSubCommands are implemented both in local and remote clients var imageSubCommands = []*cobra.Command{ + _buildCommand, _historyCommand, _imageExistsCommand, _imagesCommand, diff --git a/cmd/podman/login.go b/cmd/podman/login.go index 0bd58ff78..1fc4d5327 100644 --- a/cmd/podman/login.go +++ b/cmd/podman/login.go @@ -44,6 +44,7 @@ func init() { flags.StringVarP(&loginCommand.Password, "password", "p", "", "Password for registry") flags.BoolVar(&loginCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") flags.StringVarP(&loginCommand.Username, "username", "u", "", "Username for registry") + flags.BoolVar(&loginCommand.StdinPassword, "password-stdin", false, "Take the password from stdin") } @@ -90,8 +91,26 @@ func loginCmd(c *cliconfig.LoginValues) error { } ctx := getContext() + + password := c.Password + + if c.Flag("password-stdin").Changed { + var stdinPasswordStrBuilder strings.Builder + if c.Password != "" { + return errors.Errorf("Can't specify both --password-stdin and --password") + } + if c.Username == "" { + return errors.Errorf("Must provide --username with --password-stdin") + } + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + fmt.Fprint(&stdinPasswordStrBuilder, scanner.Text()) + } + password = stdinPasswordStrBuilder.String() + } + // If no username and no password is specified, try to use existing ones. - if c.Username == "" && c.Password == "" { + if c.Username == "" && password == "" { fmt.Println("Authenticating with existing credentials...") if err := docker.CheckAuth(ctx, sc, userFromAuthFile, passFromAuthFile, server); err == nil { fmt.Println("Existing credentials are valid. Already logged in to", server) @@ -100,7 +119,7 @@ func loginCmd(c *cliconfig.LoginValues) error { fmt.Println("Existing credentials are invalid, please enter valid username and password") } - username, password, err := getUserAndPass(c.Username, c.Password, userFromAuthFile) + username, password, err := getUserAndPass(c.Username, password, userFromAuthFile) if err != nil { return errors.Wrapf(err, "error getting username and password") } diff --git a/cmd/podman/main.go b/cmd/podman/main.go index a6f0c500a..f9820c075 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -30,6 +30,7 @@ var ( // Commands that the remote and local client have // implemented. var mainCommands = []*cobra.Command{ + _buildCommand, _exportCommand, _historyCommand, _imagesCommand, diff --git a/cmd/podman/rm.go b/cmd/podman/rm.go index 1e5e9d254..d170e5357 100644 --- a/cmd/podman/rm.go +++ b/cmd/podman/rm.go @@ -39,8 +39,7 @@ func init() { flags.BoolVarP(&rmCommand.All, "all", "a", false, "Remove all containers") flags.BoolVarP(&rmCommand.Force, "force", "f", false, "Force removal of a running container. The default is false") flags.BoolVarP(&rmCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - flags.BoolVarP(&rmCommand.Volumes, "volumes", "v", false, "Remove the volumes associated with the container (Not implemented yet)") - + flags.BoolVarP(&rmCommand.Volumes, "volumes", "v", false, "Remove the volumes associated with the container") } // saveCmd saves the image to either docker-archive or oci @@ -79,7 +78,7 @@ func rmCmd(c *cliconfig.RmValues) error { for _, container := range delContainers { con := container f := func() error { - return runtime.RemoveContainer(ctx, con, c.Force) + return runtime.RemoveContainer(ctx, con, c.Force, c.Volumes) } deleteFuncs = append(deleteFuncs, shared.ParallelWorkerInput{ diff --git a/cmd/podman/run.go b/cmd/podman/run.go index 16ec7c3c0..64f8b6856 100644 --- a/cmd/podman/run.go +++ b/cmd/podman/run.go @@ -132,7 +132,7 @@ func runCmd(c *cliconfig.RunValues) error { exitCode = 126 } if c.IsSet("rm") { - if deleteError := runtime.RemoveContainer(ctx, ctr, true); deleteError != nil { + if deleteError := runtime.RemoveContainer(ctx, ctr, true, false); deleteError != nil { logrus.Errorf("unable to remove container %s after failing to start and attach to it", ctr.ID()) } } diff --git a/cmd/podman/start.go b/cmd/podman/start.go index d1434508d..3a606d662 100644 --- a/cmd/podman/start.go +++ b/cmd/podman/start.go @@ -144,7 +144,7 @@ func startCmd(c *cliconfig.StartValues) error { logrus.Errorf("unable to detect if container %s should be deleted", ctr.ID()) } if createArtifact.Rm { - if rmErr := runtime.RemoveContainer(ctx, ctr, true); rmErr != nil { + if rmErr := runtime.RemoveContainer(ctx, ctr, true, false); rmErr != nil { logrus.Errorf("unable to remove container %s after it failed to start", ctr.ID()) } } diff --git a/cmd/podman/system_prune.go b/cmd/podman/system_prune.go index a88027558..a91d7bf0a 100644 --- a/cmd/podman/system_prune.go +++ b/cmd/podman/system_prune.go @@ -76,7 +76,7 @@ Are you sure you want to continue? [y/N] `, volumeString) ctx := getContext() fmt.Println("Deleted Containers") - lasterr := pruneContainers(runtime, ctx, shared.Parallelize("rm"), false) + lasterr := pruneContainers(runtime, ctx, shared.Parallelize("rm"), false, false) if c.Bool("volumes") { fmt.Println("Deleted Volumes") err := volumePrune(runtime, getContext()) diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index 94947c0e0..d6d9936f2 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -600,7 +600,7 @@ method GetAttachSockets(name: string) -> (sockets: Sockets) # a [ContainerNotFound](#ContainerNotFound) error is returned. method WaitContainer(name: string) -> (exitcode: int) -# RemoveContainer takes requires the name or ID of container as well a boolean representing whether a running +# RemoveContainer takes requires the name or ID of container as well a boolean representing whether a running and a boolean indicating whether to remove builtin volumes # container can be stopped and removed. Upon successful removal of the container, its ID is returned. If the # container cannot be found by name or ID, a [ContainerNotFound](#ContainerNotFound) error will be returned. # #### Example @@ -610,7 +610,7 @@ method WaitContainer(name: string) -> (exitcode: int) # "container": "62f4fd98cb57f529831e8f90610e54bba74bd6f02920ffb485e15376ed365c20" # } # ~~~ -method RemoveContainer(name: string, force: bool) -> (container: string) +method RemoveContainer(name: string, force: bool, removeVolumes: bool) -> (container: string) # DeleteStoppedContainers will delete all containers that are not running. It will return a list the deleted # container IDs. See also [RemoveContainer](RemoveContainer). |