mirror of
https://github.com/containers/podman.git
synced 2025-06-05 22:31:06 +08:00
14
README.md
14
README.md
@ -71,20 +71,6 @@ A little configuration by an administrator is required before rootless Podman ca
|
||||
See [Skopeo](https://github.com/containers/skopeo/) for those tasks.
|
||||
* Support for the Kubernetes CRI interface for container management.
|
||||
The [CRI-O](https://github.com/cri-o/cri-o) daemon specializes in that.
|
||||
* Supporting `docker-compose`. We believe that Kubernetes is the defacto
|
||||
standard for composing Pods and for orchestrating containers, making
|
||||
Kubernetes YAML a defacto standard file format. Hence, Podman allows the
|
||||
creation and execution of Pods from a Kubernetes YAML file (see
|
||||
[podman-play-kube](https://github.com/containers/podman/blob/master/docs/source/markdown/podman-play-kube.1.md)).
|
||||
Podman can also generate Kubernetes YAML based on a container or Pod (see
|
||||
[podman-generate-kube](https://github.com/containers/podman/blob/master/docs/source/markdown/podman-generate-kube.1.md)),
|
||||
which allows for an easy transition from a local development environment
|
||||
to a production Kubernetes cluster. If Kubernetes does not fit your requirements,
|
||||
there are other third-party tools that support the docker-compose format such as
|
||||
[kompose](https://github.com/kubernetes/kompose/) and
|
||||
[podman-compose](https://github.com/muayyad-alsadi/podman-compose)
|
||||
that might be appropriate for your environment. This situation may change with
|
||||
the addition of the REST API.
|
||||
|
||||
## OCI Projects Plans
|
||||
|
||||
|
56
cmd/podman/containers/rename.go
Normal file
56
cmd/podman/containers/rename.go
Normal file
@ -0,0 +1,56 @@
|
||||
package containers
|
||||
|
||||
import (
|
||||
"github.com/containers/podman/v2/cmd/podman/common"
|
||||
"github.com/containers/podman/v2/cmd/podman/registry"
|
||||
"github.com/containers/podman/v2/pkg/domain/entities"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
renameDescription = "The podman rename command allows you to rename an existing container"
|
||||
renameCommand = &cobra.Command{
|
||||
Use: "rename CONTAINER NAME",
|
||||
Short: "Rename an existing container",
|
||||
Long: renameDescription,
|
||||
RunE: rename,
|
||||
Args: cobra.ExactArgs(2),
|
||||
ValidArgsFunction: common.AutocompletePortCommand,
|
||||
Example: "podman rename containerA newName",
|
||||
}
|
||||
|
||||
containerRenameCommand = &cobra.Command{
|
||||
Use: renameCommand.Use,
|
||||
Short: renameCommand.Short,
|
||||
Long: renameCommand.Long,
|
||||
RunE: renameCommand.RunE,
|
||||
Args: renameCommand.Args,
|
||||
ValidArgsFunction: renameCommand.ValidArgsFunction,
|
||||
Example: "podman container rename containerA newName",
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
// TODO: Once bindings are done, add this to TunnelMode
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||
Mode: []entities.EngineMode{entities.ABIMode},
|
||||
Command: renameCommand,
|
||||
})
|
||||
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||
Mode: []entities.EngineMode{entities.ABIMode},
|
||||
Command: containerRenameCommand,
|
||||
Parent: containerCmd,
|
||||
})
|
||||
}
|
||||
|
||||
func rename(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 2 {
|
||||
return errors.Errorf("must provide at least two arguments to rename")
|
||||
}
|
||||
renameOpts := entities.ContainerRenameOptions{
|
||||
NewName: args[1],
|
||||
}
|
||||
return registry.ContainerEngine().ContainerRename(registry.GetContext(), args[0], renameOpts)
|
||||
}
|
@ -75,6 +75,8 @@ Commands
|
||||
|
||||
:doc:`push <markdown/podman-push.1>` Push an image to a specified destination
|
||||
|
||||
:doc:`rename <markdown/podman-rename.1>` Rename an existing container
|
||||
|
||||
:doc:`restart <markdown/podman-restart.1>` Restart one or more containers
|
||||
|
||||
:doc:`rm <markdown/podman-rm.1>` Remove one or more containers
|
||||
|
@ -41,6 +41,8 @@ Manage Containers
|
||||
|
||||
:doc:`ps <markdown/podman-ps.1>` List containers
|
||||
|
||||
:doc:`rename <markdown/podman-rename.1>` Rename an existing container
|
||||
|
||||
:doc:`restart <markdown/podman-restart.1>` Restart one or more containers
|
||||
|
||||
:doc:`restore <markdown/podman-container-restore.1>` Restores one or more containers from a checkpoint
|
||||
|
@ -33,6 +33,7 @@ The container command allows you to manage containers
|
||||
| port | [podman-port(1)](podman-port.1.md) | List port mappings for the container. |
|
||||
| prune | [podman-container-prune(1)](podman-container-prune.1.md)| Remove all stopped containers from local storage. |
|
||||
| ps | [podman-ps(1)](podman-ps.1.md) | Prints out information about containers. |
|
||||
| rename | [podman-rename(1)](podman-rename.1.md) | Rename an existing container. |
|
||||
| restart | [podman-restart(1)](podman-restart.1.md) | Restart one or more containers. |
|
||||
| restore | [podman-container-restore(1)](podman-container-restore.1.md) | Restores one or more containers from a checkpoint. |
|
||||
| rm | [podman-rm(1)](podman-rm.1.md) | Remove one or more containers. |
|
||||
@ -42,7 +43,7 @@ The container command allows you to manage containers
|
||||
| stats | [podman-stats(1)](podman-stats.1.md) | Display a live stream of one or more container's resource usage statistics. |
|
||||
| stop | [podman-stop(1)](podman-stop.1.md) | Stop one or more running containers. |
|
||||
| top | [podman-top(1)](podman-top.1.md) | Display the running processes of a container. |
|
||||
| unmount | [podman-unmount(1)](podman-unmount.1.md) | Unmount a working container's root filesystem.(Alias unmount) |
|
||||
| unmount | [podman-unmount(1)](podman-unmount.1.md) | Unmount a working container's root filesystem.(Alias unmount) |
|
||||
| unpause | [podman-unpause(1)](podman-unpause.1.md) | Unpause one or more containers. |
|
||||
| wait | [podman-wait(1)](podman-wait.1.md) | Wait on one or more containers to stop and print their exit codes. |
|
||||
|
||||
|
38
docs/source/markdown/podman-rename.1.md
Normal file
38
docs/source/markdown/podman-rename.1.md
Normal file
@ -0,0 +1,38 @@
|
||||
% podman-rename(1)
|
||||
|
||||
## NAME
|
||||
podman\-rename - Rename an existing container
|
||||
|
||||
## SYNOPSIS
|
||||
**podman rename** *container* *newname*
|
||||
|
||||
**podman container rename** *container* *newname*
|
||||
|
||||
## DESCRIPTION
|
||||
Rename changes the name of an existing container.
|
||||
The old name will be freed, and will be available for use.
|
||||
This command can be run on containers in any state.
|
||||
However, running containers may not fully receive the effects until they are restarted - for example, a running container may still use the old name in its logs.
|
||||
At present, only containers are supported; pods and volumes cannot be renamed.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
```
|
||||
# Rename a container by name
|
||||
$ podman rename oldContainer aNewName
|
||||
```
|
||||
|
||||
```
|
||||
# Rename a container by ID
|
||||
$ podman rename 717716c00a6b testcontainer
|
||||
```
|
||||
|
||||
```
|
||||
# Use the container rename alias
|
||||
$ podman container rename 6e7514b47180 databaseCtr
|
||||
```
|
||||
|
||||
## SEE ALSO
|
||||
podman(1), podman-create(1), podman-run(1)
|
@ -247,6 +247,7 @@ the exit codes follow the `chroot` standard, see below:
|
||||
| [podman-ps(1)](podman-ps.1.md) | Prints out information about containers. |
|
||||
| [podman-pull(1)](podman-pull.1.md) | Pull an image from a registry. |
|
||||
| [podman-push(1)](podman-push.1.md) | Push an image from local storage to elsewhere. |
|
||||
| [podman-rename(1)](podman-rename.1.md) | Rename an existing container. |
|
||||
| [podman-restart(1)](podman-restart.1.md) | Restart one or more containers. |
|
||||
| [podman-rm(1)](podman-rm.1.md) | Remove one or more containers. |
|
||||
| [podman-rmi(1)](podman-rmi.1.md) | Removes one or more locally stored images. |
|
||||
@ -259,7 +260,7 @@ the exit codes follow the `chroot` standard, see below:
|
||||
| [podman-system(1)](podman-system.1.md) | Manage podman. |
|
||||
| [podman-tag(1)](podman-tag.1.md) | Add an additional name to a local image. |
|
||||
| [podman-top(1)](podman-top.1.md) | Display the running processes of a container. |
|
||||
| [podman-unmount(1)](podman-unmount.1.md) | Unmount a working container's root filesystem. |
|
||||
| [podman-unmount(1)](podman-unmount.1.md) | Unmount a working container's root filesystem. |
|
||||
| [podman-unpause(1)](podman-unpause.1.md) | Unpause one or more containers. |
|
||||
| [podman-unshare(1)](podman-unshare.1.md) | Run a command inside of a modified user namespace. |
|
||||
| [podman-untag(1)](podman-untag.1.md) | Removes one or more names from a locally-stored image. |
|
||||
|
@ -72,6 +72,140 @@ func (r *Runtime) RestoreContainer(ctx context.Context, rSpec *spec.Spec, config
|
||||
return r.setupContainer(ctx, ctr)
|
||||
}
|
||||
|
||||
// RenameContainer renames the given container.
|
||||
// The given container object will be rendered unusable, and a new, renamed
|
||||
// Container will be returned.
|
||||
func (r *Runtime) RenameContainer(ctx context.Context, ctr *Container, newName string) (*Container, error) {
|
||||
ctr.lock.Lock()
|
||||
defer ctr.lock.Unlock()
|
||||
|
||||
if err := ctr.syncContainer(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if newName == "" || !define.NameRegex.MatchString(newName) {
|
||||
return nil, define.RegexError
|
||||
}
|
||||
|
||||
// Check if the name is available.
|
||||
// This is *100% NOT ATOMIC* so any failures in-flight will do
|
||||
// *VERY BAD THINGS* to the state. So we have to try and catch all we
|
||||
// can before starting.
|
||||
if _, err := r.state.LookupContainerID(newName); err == nil {
|
||||
return nil, errors.Wrapf(define.ErrCtrExists, "name %s is already in use by another container", newName)
|
||||
}
|
||||
if _, err := r.state.LookupPod(newName); err == nil {
|
||||
return nil, errors.Wrapf(define.ErrPodExists, "name %s is already in use by another pod", newName)
|
||||
}
|
||||
|
||||
// TODO: Investigate if it is possible to remove this limitation.
|
||||
depCtrs, err := r.state.ContainerInUse(ctr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(depCtrs) > 0 {
|
||||
return nil, errors.Wrapf(define.ErrCtrExists, "cannot rename container %s as it is in use by other containers: %v", ctr.ID(), strings.Join(depCtrs, ","))
|
||||
}
|
||||
|
||||
// We need to pull an updated config, in case another rename fired and
|
||||
// the config was re-written.
|
||||
newConf, err := r.state.GetContainerConfig(ctr.ID())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error retrieving container %s configuration from DB to remove", ctr.ID())
|
||||
}
|
||||
ctr.config = newConf
|
||||
|
||||
// TODO: This is going to fail if we have active exec sessions, too.
|
||||
// Investigate fixing that at a later date.
|
||||
|
||||
var pod *Pod
|
||||
if ctr.config.Pod != "" {
|
||||
tmpPod, err := r.state.Pod(ctr.config.Pod)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error retrieving container %s pod", ctr.ID())
|
||||
}
|
||||
pod = tmpPod
|
||||
// Lock pod to ensure it's not removed while we're working
|
||||
pod.lock.Lock()
|
||||
defer pod.lock.Unlock()
|
||||
}
|
||||
|
||||
// Lock all volumes to ensure they are not removed while we're working
|
||||
volsLocked := make(map[string]bool)
|
||||
for _, namedVol := range ctr.config.NamedVolumes {
|
||||
if volsLocked[namedVol.Name] {
|
||||
continue
|
||||
}
|
||||
vol, err := r.state.Volume(namedVol.Name)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error retrieving volume used by container %s", ctr.ID())
|
||||
}
|
||||
|
||||
volsLocked[vol.Name()] = true
|
||||
vol.lock.Lock()
|
||||
defer vol.lock.Unlock()
|
||||
}
|
||||
|
||||
logrus.Infof("Going to rename container %s from %q to %q", ctr.ID(), ctr.Name(), newName)
|
||||
|
||||
// Step 1: remove the old container.
|
||||
if pod != nil {
|
||||
if err := r.state.RemoveContainerFromPod(pod, ctr); err != nil {
|
||||
return nil, errors.Wrapf(err, "error renaming container %s", ctr.ID())
|
||||
}
|
||||
} else {
|
||||
if err := r.state.RemoveContainer(ctr); err != nil {
|
||||
return nil, errors.Wrapf(err, "error renaming container %s", ctr.ID())
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Make a new container based on the old one.
|
||||
// TODO: Should we deep-copy the container config and state, to be safe?
|
||||
newCtr := new(Container)
|
||||
newCtr.config = ctr.config
|
||||
newCtr.state = ctr.state
|
||||
newCtr.lock = ctr.lock
|
||||
newCtr.ociRuntime = ctr.ociRuntime
|
||||
newCtr.runtime = r
|
||||
newCtr.rootlessSlirpSyncR = ctr.rootlessSlirpSyncR
|
||||
newCtr.rootlessSlirpSyncW = ctr.rootlessSlirpSyncW
|
||||
newCtr.rootlessPortSyncR = ctr.rootlessPortSyncR
|
||||
newCtr.rootlessPortSyncW = ctr.rootlessPortSyncW
|
||||
|
||||
newCtr.valid = true
|
||||
newCtr.config.Name = newName
|
||||
|
||||
// Step 3: Add that new container to the DB
|
||||
if pod != nil {
|
||||
if err := r.state.AddContainerToPod(pod, newCtr); err != nil {
|
||||
return nil, errors.Wrapf(err, "error renaming container %s", newCtr.ID())
|
||||
}
|
||||
} else {
|
||||
if err := r.state.AddContainer(newCtr); err != nil {
|
||||
return nil, errors.Wrapf(err, "error renaming container %s", newCtr.ID())
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: Save the new container, to force the state to be written to
|
||||
// the DB. This may not be necessary, depending on DB implementation,
|
||||
// but let's do it to be safe.
|
||||
if err := newCtr.save(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Step 5: rename the container in c/storage.
|
||||
// This can fail if the name is already in use by a non-Podman
|
||||
// container. This puts us in a bad spot - we've already renamed the
|
||||
// container in Podman. We can swap the order, but then we have the
|
||||
// opposite problem. Atomicity is a real problem here, with no easy
|
||||
// solution.
|
||||
if err := r.store.SetNames(newCtr.ID(), []string{newCtr.Name()}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newCtr, nil
|
||||
}
|
||||
|
||||
func (r *Runtime) initContainerVariables(rSpec *spec.Spec, config *ContainerConfig) (*Container, error) {
|
||||
if rSpec == nil {
|
||||
return nil, errors.Wrapf(define.ErrInvalidArg, "must provide a valid runtime spec to create container")
|
||||
@ -393,7 +527,7 @@ func (r *Runtime) RemoveContainer(ctx context.Context, c *Container, force bool,
|
||||
// removePod is used only when removing pods. It instructs Podman to ignore
|
||||
// infra container protections, and *not* remove from the database (as pod
|
||||
// remove will handle that).
|
||||
func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool, removeVolume bool, removePod bool) error {
|
||||
func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, removeVolume, removePod bool) error {
|
||||
span, _ := opentracing.StartSpanFromContext(ctx, "removeContainer")
|
||||
span.SetTag("type", "runtime")
|
||||
defer span.Finish()
|
||||
@ -406,6 +540,18 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool,
|
||||
}
|
||||
}
|
||||
|
||||
// We need to refresh container config from the DB, to ensure that any
|
||||
// changes (e.g. a rename) are picked up before we start removing.
|
||||
// Since HasContainer above succeeded, we can safely assume the
|
||||
// container exists.
|
||||
// This is *very iffy* but it should be OK because the container won't
|
||||
// exist once we're done.
|
||||
newConf, err := r.state.GetContainerConfig(c.ID())
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error retrieving container %s configuration from DB to remove", c.ID())
|
||||
}
|
||||
c.config = newConf
|
||||
|
||||
logrus.Debugf("Removing container %s", c.ID())
|
||||
|
||||
// We need to lock the pod before we lock the container.
|
||||
@ -413,7 +559,6 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool,
|
||||
// Don't need to do this in pod removal case - we're evicting the entire
|
||||
// pod.
|
||||
var pod *Pod
|
||||
var err error
|
||||
runtime := c.runtime
|
||||
if c.config.Pod != "" && !removePod {
|
||||
pod, err = r.state.Pod(c.config.Pod)
|
||||
|
@ -465,3 +465,34 @@ func formatCapabilities(slice []string) {
|
||||
slice[i] = strings.TrimPrefix(slice[i], "CAP_")
|
||||
}
|
||||
}
|
||||
|
||||
func RenameContainer(w http.ResponseWriter, r *http.Request) {
|
||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||
|
||||
name := utils.GetName(r)
|
||||
query := struct {
|
||||
Name string `schema:"name"`
|
||||
}{}
|
||||
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
||||
utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
|
||||
return
|
||||
}
|
||||
|
||||
ctr, err := runtime.LookupContainer(name)
|
||||
if err != nil {
|
||||
utils.ContainerNotFound(w, name, err)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := runtime.RenameContainer(r.Context(), ctr, query.Name); err != nil {
|
||||
if errors.Cause(err) == define.ErrPodExists || errors.Cause(err) == define.ErrCtrExists {
|
||||
utils.Error(w, "Something went wrong.", http.StatusConflict, err)
|
||||
return
|
||||
}
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
utils.WriteResponse(w, http.StatusNoContent, nil)
|
||||
}
|
||||
|
@ -291,9 +291,6 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
|
||||
r.HandleFunc(VersionedPath("/containers/{name}/pause"), s.APIHandler(compat.PauseContainer)).Methods(http.MethodPost)
|
||||
// Added non version path to URI to support docker non versioned paths
|
||||
r.HandleFunc("/containers/{name}/pause", s.APIHandler(compat.PauseContainer)).Methods(http.MethodPost)
|
||||
r.HandleFunc(VersionedPath("/containers/{name}/rename"), s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodPost)
|
||||
// Added non version path to URI to support docker non versioned paths
|
||||
r.HandleFunc("/containers/{name}/rename", s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodPost)
|
||||
// swagger:operation POST /containers/{name}/restart compat restartContainer
|
||||
// ---
|
||||
// tags:
|
||||
@ -610,6 +607,36 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
|
||||
// $ref: "#/responses/InternalError"
|
||||
r.HandleFunc(VersionedPath("/containers/{name}/export"), s.APIHandler(compat.ExportContainer)).Methods(http.MethodGet)
|
||||
r.HandleFunc("/containers/{name}/export", s.APIHandler(compat.ExportContainer)).Methods(http.MethodGet)
|
||||
// swagger:operation POST /containers/{name}/rename compat renameContainer
|
||||
// ---
|
||||
// tags:
|
||||
// - containers (compat)
|
||||
// summary: Rename an existing container
|
||||
// description: Change the name of an existing container.
|
||||
// parameters:
|
||||
// - in: path
|
||||
// name: name
|
||||
// type: string
|
||||
// required: true
|
||||
// description: Full or partial ID or full name of the container to rename
|
||||
// - in: query
|
||||
// name: name
|
||||
// type: string
|
||||
// required: true
|
||||
// description: New name for the container
|
||||
// produces:
|
||||
// - application/json
|
||||
// responses:
|
||||
// 204:
|
||||
// description: no error
|
||||
// 404:
|
||||
// $ref: "#/responses/NoSuchContainer"
|
||||
// 409:
|
||||
// $ref: "#/responses/ConflictError"
|
||||
// 500:
|
||||
// $ref: "#/responses/InternalError"
|
||||
r.HandleFunc(VersionedPath("/containers/{name}/rename"), s.APIHandler(compat.RenameContainer)).Methods(http.MethodPost)
|
||||
r.HandleFunc("/containers/{name}/rename", s.APIHandler(compat.RenameContainer)).Methods(http.MethodPost)
|
||||
|
||||
/*
|
||||
libpod endpoints
|
||||
@ -1463,5 +1490,34 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
|
||||
// 500:
|
||||
// $ref: "#/responses/InternalError"
|
||||
r.HandleFunc(VersionedPath("/libpod/containers/{name}/init"), s.APIHandler(libpod.InitContainer)).Methods(http.MethodPost)
|
||||
// swagger:operation POST /libpod/containers/{name}/rename libpod libpodRenameContainer
|
||||
// ---
|
||||
// tags:
|
||||
// - containers
|
||||
// summary: Rename an existing container
|
||||
// description: Change the name of an existing container.
|
||||
// parameters:
|
||||
// - in: path
|
||||
// name: name
|
||||
// type: string
|
||||
// required: true
|
||||
// description: Full or partial ID or full name of the container to rename
|
||||
// - in: query
|
||||
// name: name
|
||||
// type: string
|
||||
// required: true
|
||||
// description: New name for the container
|
||||
// produces:
|
||||
// - application/json
|
||||
// responses:
|
||||
// 204:
|
||||
// description: no error
|
||||
// 404:
|
||||
// $ref: "#/responses/NoSuchContainer"
|
||||
// 409:
|
||||
// $ref: "#/responses/ConflictError"
|
||||
// 500:
|
||||
// $ref: "#/responses/InternalError"
|
||||
r.HandleFunc(VersionedPath("/libpod/containers/{name}/rename"), s.APIHandler(compat.RenameContainer)).Methods(http.MethodPost)
|
||||
return nil
|
||||
}
|
||||
|
@ -434,3 +434,9 @@ type ContainerStatsReport struct {
|
||||
// Results, set when there is no error.
|
||||
Stats []define.ContainerStats
|
||||
}
|
||||
|
||||
// ContainerRenameOptions describes input options for renaming a container.
|
||||
type ContainerRenameOptions struct {
|
||||
// NewName is the new name that will be given to the container.
|
||||
NewName string
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ type ContainerEngine interface {
|
||||
ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
|
||||
ContainerPort(ctx context.Context, nameOrID string, options ContainerPortOptions) ([]*ContainerPortReport, error)
|
||||
ContainerPrune(ctx context.Context, options ContainerPruneOptions) ([]*reports.PruneReport, error)
|
||||
ContainerRename(ctr context.Context, nameOrID string, options ContainerRenameOptions) error
|
||||
ContainerRestart(ctx context.Context, namesOrIds []string, options RestartOptions) ([]*RestartReport, error)
|
||||
ContainerRestore(ctx context.Context, namesOrIds []string, options RestoreOptions) ([]*RestoreReport, error)
|
||||
ContainerRm(ctx context.Context, namesOrIds []string, options RmOptions) ([]*RmReport, error)
|
||||
|
@ -902,7 +902,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
|
||||
if err := ic.Libpod.RemoveContainer(ctx, ctr, false, true); err != nil {
|
||||
if errors.Cause(err) == define.ErrNoSuchCtr ||
|
||||
errors.Cause(err) == define.ErrCtrRemoved {
|
||||
logrus.Warnf("Container %s does not exist: %v", ctr.ID(), err)
|
||||
logrus.Infof("Container %s was already removed, skipping --rm", ctr.ID())
|
||||
} else {
|
||||
logrus.Errorf("Error removing container %s: %v", ctr.ID(), err)
|
||||
}
|
||||
@ -1312,3 +1312,17 @@ func (ic *ContainerEngine) ShouldRestart(ctx context.Context, nameOrID string) (
|
||||
|
||||
return &entities.BoolReport{Value: ctr.ShouldRestart(ctx)}, nil
|
||||
}
|
||||
|
||||
// ContainerRename renames the given container.
|
||||
func (ic *ContainerEngine) ContainerRename(ctx context.Context, nameOrID string, opts entities.ContainerRenameOptions) error {
|
||||
ctr, err := ic.Libpod.LookupContainer(nameOrID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := ic.Libpod.RenameContainer(ctx, ctr, opts.NewName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -820,3 +820,8 @@ func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []stri
|
||||
func (ic *ContainerEngine) ShouldRestart(_ context.Context, id string) (bool, error) {
|
||||
return containers.ShouldRestart(ic.ClientCtx, id, nil)
|
||||
}
|
||||
|
||||
// ContainerRename renames the given container.
|
||||
func (ic *ContainerEngine) ContainerRename(ctx context.Context, nameOrID string, opts entities.ContainerRenameOptions) error {
|
||||
return errors.Errorf("NOT YET IMPLEMENTED")
|
||||
}
|
||||
|
93
test/e2e/rename_test.go
Normal file
93
test/e2e/rename_test.go
Normal file
@ -0,0 +1,93 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
. "github.com/containers/podman/v2/test/utils"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("podman rename", func() {
|
||||
var (
|
||||
tempdir string
|
||||
err error
|
||||
podmanTest *PodmanTestIntegration
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
SkipIfRemote("Rename not yet implemented by podman-remote")
|
||||
tempdir, err = CreateTempDirInTempDir()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
podmanTest = PodmanTestCreate(tempdir)
|
||||
podmanTest.Setup()
|
||||
podmanTest.SeedImages()
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
podmanTest.Cleanup()
|
||||
f := CurrentGinkgoTestDescription()
|
||||
processTestResult(f)
|
||||
|
||||
})
|
||||
|
||||
It("podman rename on non-existent container", func() {
|
||||
session := podmanTest.Podman([]string{"rename", "doesNotExist", "aNewName"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Not(Equal(0)))
|
||||
})
|
||||
|
||||
It("Podman rename on existing container with bad name", func() {
|
||||
ctrName := "testCtr"
|
||||
ctr := podmanTest.Podman([]string{"create", "--name", ctrName, ALPINE, "top"})
|
||||
ctr.WaitWithDefaultTimeout()
|
||||
Expect(ctr.ExitCode()).To(Equal(0))
|
||||
|
||||
newName := "invalid<>:char"
|
||||
rename := podmanTest.Podman([]string{"rename", ctrName, newName})
|
||||
rename.WaitWithDefaultTimeout()
|
||||
Expect(rename.ExitCode()).To(Not(Equal(0)))
|
||||
|
||||
ps := podmanTest.Podman([]string{"ps", "-aq", "--filter", fmt.Sprintf("name=%s", ctrName), "--format", "{{ .Names }}"})
|
||||
ps.WaitWithDefaultTimeout()
|
||||
Expect(ps.ExitCode()).To(Equal(0))
|
||||
Expect(ps.OutputToString()).To(ContainSubstring(ctrName))
|
||||
})
|
||||
|
||||
It("Successfully rename a created container", func() {
|
||||
ctrName := "testCtr"
|
||||
ctr := podmanTest.Podman([]string{"create", "--name", ctrName, ALPINE, "top"})
|
||||
ctr.WaitWithDefaultTimeout()
|
||||
Expect(ctr.ExitCode()).To(Equal(0))
|
||||
|
||||
newName := "aNewName"
|
||||
rename := podmanTest.Podman([]string{"rename", ctrName, newName})
|
||||
rename.WaitWithDefaultTimeout()
|
||||
Expect(rename.ExitCode()).To(Equal(0))
|
||||
|
||||
ps := podmanTest.Podman([]string{"ps", "-aq", "--filter", fmt.Sprintf("name=%s", newName), "--format", "{{ .Names }}"})
|
||||
ps.WaitWithDefaultTimeout()
|
||||
Expect(ps.ExitCode()).To(Equal(0))
|
||||
Expect(ps.OutputToString()).To(ContainSubstring(newName))
|
||||
})
|
||||
|
||||
It("Successfully rename a running container", func() {
|
||||
ctrName := "testCtr"
|
||||
ctr := podmanTest.Podman([]string{"run", "-d", "--name", ctrName, ALPINE, "top"})
|
||||
ctr.WaitWithDefaultTimeout()
|
||||
Expect(ctr.ExitCode()).To(Equal(0))
|
||||
|
||||
newName := "aNewName"
|
||||
rename := podmanTest.Podman([]string{"rename", ctrName, newName})
|
||||
rename.WaitWithDefaultTimeout()
|
||||
Expect(rename.ExitCode()).To(Equal(0))
|
||||
|
||||
ps := podmanTest.Podman([]string{"ps", "-aq", "--filter", fmt.Sprintf("name=%s", newName), "--format", "{{ .Names }}"})
|
||||
ps.WaitWithDefaultTimeout()
|
||||
Expect(ps.ExitCode()).To(Equal(0))
|
||||
Expect(ps.OutputToString()).To(ContainSubstring(newName))
|
||||
})
|
||||
})
|
Reference in New Issue
Block a user