diff --git a/cmd/kpod/common.go b/cmd/kpod/common.go index aa38b9f7..8dc51237 100644 --- a/cmd/kpod/common.go +++ b/cmd/kpod/common.go @@ -53,7 +53,9 @@ func getConfig(c *cli.Context) (*libkpod.Config, error) { if c.Bool("debug") { logrus.SetLevel(logrus.DebugLevel) } - + if c.GlobalIsSet("runtime") { + config.Runtime = c.GlobalString("runtime") + } return config, nil } diff --git a/cmd/kpod/main.go b/cmd/kpod/main.go index 5ae2a6a6..34fd6c44 100644 --- a/cmd/kpod/main.go +++ b/cmd/kpod/main.go @@ -34,6 +34,7 @@ func main() { mountCommand, pullCommand, pushCommand, + renameCommand, rmiCommand, saveCommand, tagCommand, @@ -57,6 +58,10 @@ func main() { Name: "runroot", Usage: "path to the 'run directory' where all state information is stored", }, + cli.StringFlag{ + Name: "runtime", + Usage: "path to the OCI-compatible binary used to run containers, default is /usr/bin/runc", + }, cli.StringFlag{ Name: "storage-driver, s", Usage: "select which storage driver is used to manage storage of images and containers (default is overlay2)", diff --git a/cmd/kpod/rename.go b/cmd/kpod/rename.go new file mode 100644 index 00000000..8ef63419 --- /dev/null +++ b/cmd/kpod/rename.go @@ -0,0 +1,45 @@ +package main + +import ( + "github.com/kubernetes-incubator/cri-o/libkpod" + "github.com/pkg/errors" + "github.com/urfave/cli" +) + +var ( + renameDescription = "Rename a container. Container may be created, running, paused, or stopped" + renameFlags = []cli.Flag{} + renameCommand = cli.Command{ + Name: "rename", + Usage: "rename a container", + Description: renameDescription, + Action: renameCmd, + ArgsUsage: "CONTAINER NEW-NAME", + Flags: renameFlags, + } +) + +func renameCmd(c *cli.Context) error { + if len(c.Args()) != 2 { + return errors.Errorf("Rename requires a src container name/ID and a dest container name") + } + + config, err := getConfig(c) + if err != nil { + return errors.Wrapf(err, "Could not get config") + } + server, err := libkpod.New(config) + if err != nil { + return errors.Wrapf(err, "could not get container server") + } + err = server.Update() + if err != nil { + return errors.Wrapf(err, "could not update list of containers") + } + + err = server.ContainerRename(c.Args().Get(0), c.Args().Get(1)) + if err != nil { + return errors.Wrapf(err, "could not rename container") + } + return nil +} diff --git a/completions/bash/kpod b/completions/bash/kpod index 15ef1a18..ab3fa11b 100644 --- a/completions/bash/kpod +++ b/completions/bash/kpod @@ -181,6 +181,26 @@ _kpod_push() { esac } +_kpod_rename() { + local boolean_options=" + --help + -h + " + local options_with_args=" + " + + local all_options="$options_with_args $boolean_options" + + case "$cur" in + -*) + COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur")) + ;; + *) + __kpod_list_containers + ;; + esac +} + _kpod_rmi() { local boolean_options=" --help diff --git a/docs/kpod-rename.1.md b/docs/kpod-rename.1.md new file mode 100644 index 00000000..e59dbd99 --- /dev/null +++ b/docs/kpod-rename.1.md @@ -0,0 +1,24 @@ +% kpod(1) kpod-rename - Rename a container +% Ryan Cole +# kpod-images "1" "March 2017" "kpod" + +## NAME +kpod rename - Rename a container + +## SYNOPSIS +**kpod** **rename** CONTAINER NEW-NAME + +## DESCRIPTION +Rename a container. Container may be created, running, paused, or stopped + +## EXAMPLE + +kpod rename redis-container webserver + +kpod rename a236b9a4 mycontainer + +## SEE ALSO +kpod(1) + +## HISTORY +March 2017, Originally compiled by Ryan Cole diff --git a/docs/kpod.1.md b/docs/kpod.1.md index 186ede7b..d420ab40 100644 --- a/docs/kpod.1.md +++ b/docs/kpod.1.md @@ -35,6 +35,9 @@ has the capability to debug pods/images created by crio. **--runroot**=**value** Path to the 'run directory' where all state information is stored +**--runtime**=**value** + Path to the OCI-compatible binary used to run containers + **--storage-driver, -s**=**value** Select which storage driver is used to manage storage of images and containers (default is overlay) @@ -76,6 +79,9 @@ Pull an image from a registry ### push Push an image from local storage to elsewhere +### rename +Rename a container + ### rmi Removes one or more locally stored images diff --git a/libkpod/rename.go b/libkpod/rename.go new file mode 100644 index 00000000..4a33c166 --- /dev/null +++ b/libkpod/rename.go @@ -0,0 +1,114 @@ +package libkpod + +import ( + "encoding/json" + "path/filepath" + + "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" + + "github.com/docker/docker/pkg/ioutils" + "github.com/kubernetes-incubator/cri-o/oci" + "github.com/kubernetes-incubator/cri-o/pkg/annotations" + "github.com/opencontainers/runtime-tools/generate" +) + +const configFile = "config.json" + +// ContainerRename renames the given container +func (c *ContainerServer) ContainerRename(container, name string) error { + ctr, err := c.LookupContainer(container) + if err != nil { + return err + } + + oldName := ctr.Name() + _, err = c.ReserveContainerName(ctr.ID(), name) + if err != nil { + return err + } + defer func() { + if err != nil { + c.ReleaseContainerName(name) + } else { + c.ReleaseContainerName(oldName) + } + }() + + // Update state.json + if err = c.updateStateName(ctr, name); err != nil { + return err + } + + // Update config.json + configRuntimePath := filepath.Join(ctr.BundlePath(), configFile) + if err = updateConfigName(configRuntimePath, name); err != nil { + return err + } + configStoragePath := filepath.Join(ctr.Dir(), configFile) + if err = updateConfigName(configStoragePath, name); err != nil { + return err + } + + // Update containers.json + if err = c.store.SetNames(ctr.ID(), []string{name}); err != nil { + return err + } + return nil +} + +func updateConfigName(configPath, name string) error { + specgen, err := generate.NewFromFile(configPath) + if err != nil { + return err + } + specgen.AddAnnotation(annotations.Name, name) + specgen.AddAnnotation(annotations.Metadata, updateMetadata(specgen.Spec().Annotations, name)) + + return specgen.SaveToFile(configPath, generate.ExportOptions{}) +} + +func (c *ContainerServer) updateStateName(ctr *oci.Container, name string) error { + ctr.State().Annotations[annotations.Name] = name + ctr.State().Annotations[annotations.Metadata] = updateMetadata(ctr.State().Annotations, name) + // This is taken directly from c.ContainerStateToDisk(), which can't be used because of the call to UpdateStatus() in the first line + jsonSource, err := ioutils.NewAtomicFileWriter(ctr.StatePath(), 0644) + if err != nil { + return err + } + defer jsonSource.Close() + enc := json.NewEncoder(jsonSource) + return enc.Encode(c.runtime.ContainerStatus(ctr)) +} + +// Attempts to update a metadata annotation +func updateMetadata(specAnnotations map[string]string, name string) string { + oldMetadata := specAnnotations[annotations.Metadata] + containerType := specAnnotations[annotations.ContainerType] + if containerType == "container" { + metadata := runtime.ContainerMetadata{} + err := json.Unmarshal([]byte(oldMetadata), metadata) + if err != nil { + return oldMetadata + } + metadata.Name = name + m, err := json.Marshal(metadata) + if err != nil { + return oldMetadata + } + return string(m) + } else if containerType == "sandbox" { + metadata := runtime.PodSandboxMetadata{} + err := json.Unmarshal([]byte(oldMetadata), metadata) + if err != nil { + return oldMetadata + } + metadata.Name = name + m, err := json.Marshal(metadata) + if err != nil { + return oldMetadata + } + return string(m) + } else { + return specAnnotations[annotations.Metadata] + } +} diff --git a/oci/container.go b/oci/container.go index 7907856f..14896212 100644 --- a/oci/container.go +++ b/oci/container.go @@ -160,6 +160,11 @@ func (c *Container) Sandbox() string { return c.sandbox } +// Dir returns the the dir of the container +func (c *Container) Dir() string { + return c.dir +} + // NetNsPath returns the path to the network namespace of the container. func (c *Container) NetNsPath() (string, error) { if c.state == nil { diff --git a/test/kpod_rename.bats b/test/kpod_rename.bats new file mode 100644 index 00000000..9419ce2d --- /dev/null +++ b/test/kpod_rename.bats @@ -0,0 +1,35 @@ +#!/usr/bin/env bats + +load helpers + +IMAGE="redis:alpine" +ROOT="$TESTDIR/crio" +RUNROOT="$TESTDIR/crio-run" +KPOD_OPTIONS="--root $ROOT --runroot $RUNROOT $STORAGE_OPTS" +NEW_NAME="rename-test" + +function teardown() { + cleanup_test +} + +@test "kpod rename successful" { + start_crio + run ${KPOD_BINARY} ${KPOD_OPTIONS} pull $IMAGE + [ "$status" -eq 0 ] + run crioctl pod run --config "$TESTDATA"/sandbox_config.json + echo "$output" + pod_id="$output" + [ "$status" -eq 0 ] + run crioctl ctr create --config "$TESTDATA"/container_config.json --pod "$pod_id" + ctr_id="$output" + run ${KPOD_BINARY} $KPOD_OPTIONS rename "$ctr_id" "$NEW_NAME" + echo "$output" + [ "$status" -eq 0 ] + run ${KPOD_BINARY} $KPOD_OPTIONS inspect "$ctr_id" --format {{.Name}} + echo "$output" + [ "$status" -eq 0 ] + [ "$output" == "$NEW_NAME" ] + cleanup_ctrs + cleanup_pods + stop_crio +}