containerd/cmd/ctr/utils.go
Evan Hazlett 02fa534d71 ctr: enable specifying additional environment variables
Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>
2017-05-24 18:24:31 -04:00

239 lines
6.1 KiB
Go

package main
import (
gocontext "context"
"encoding/csv"
"fmt"
"io/ioutil"
"os"
"os/signal"
"path/filepath"
"strconv"
"strings"
"syscall"
"github.com/Sirupsen/logrus"
containersapi "github.com/containerd/containerd/api/services/containers"
contentapi "github.com/containerd/containerd/api/services/content"
diffapi "github.com/containerd/containerd/api/services/diff"
"github.com/containerd/containerd/api/services/execution"
imagesapi "github.com/containerd/containerd/api/services/images"
snapshotapi "github.com/containerd/containerd/api/services/snapshot"
versionservice "github.com/containerd/containerd/api/services/version"
"github.com/containerd/containerd/api/types/task"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/images"
contentservice "github.com/containerd/containerd/services/content"
"github.com/containerd/containerd/services/diff"
imagesservice "github.com/containerd/containerd/services/images"
snapshotservice "github.com/containerd/containerd/services/snapshot"
"github.com/containerd/containerd/snapshot"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/urfave/cli"
"google.golang.org/grpc"
)
var grpcConn *grpc.ClientConn
func getContainersService(context *cli.Context) (containersapi.ContainersClient, error) {
conn, err := getGRPCConnection(context)
if err != nil {
return nil, err
}
return containersapi.NewContainersClient(conn), nil
}
func getTasksService(context *cli.Context) (execution.TasksClient, error) {
conn, err := getGRPCConnection(context)
if err != nil {
return nil, err
}
return execution.NewTasksClient(conn), nil
}
func getContentStore(context *cli.Context) (content.Store, error) {
conn, err := getGRPCConnection(context)
if err != nil {
return nil, err
}
return contentservice.NewStoreFromClient(contentapi.NewContentClient(conn)), nil
}
func getSnapshotter(context *cli.Context) (snapshot.Snapshotter, error) {
conn, err := getGRPCConnection(context)
if err != nil {
return nil, err
}
return snapshotservice.NewSnapshotterFromClient(snapshotapi.NewSnapshotClient(conn)), nil
}
func getImageStore(clicontext *cli.Context) (images.Store, error) {
conn, err := getGRPCConnection(clicontext)
if err != nil {
return nil, err
}
return imagesservice.NewStoreFromClient(imagesapi.NewImagesClient(conn)), nil
}
func getDiffService(context *cli.Context) (diff.DiffService, error) {
conn, err := getGRPCConnection(context)
if err != nil {
return nil, err
}
return diff.NewDiffServiceFromClient(diffapi.NewDiffClient(conn)), nil
}
func getVersionService(context *cli.Context) (versionservice.VersionClient, error) {
conn, err := getGRPCConnection(context)
if err != nil {
return nil, err
}
return versionservice.NewVersionClient(conn), nil
}
func getTempDir(id string) (string, error) {
err := os.MkdirAll(filepath.Join(os.TempDir(), "ctr"), 0700)
if err != nil {
return "", err
}
tmpDir, err := ioutil.TempDir(filepath.Join(os.TempDir(), "ctr"), fmt.Sprintf("%s-", id))
if err != nil {
return "", err
}
return tmpDir, nil
}
func waitContainer(events execution.Tasks_EventsClient, id string, pid uint32) (uint32, error) {
for {
e, err := events.Recv()
if err != nil {
return 255, err
}
if e.Type != task.Event_EXIT {
continue
}
if e.ID == id && e.Pid == pid {
return e.ExitStatus, nil
}
}
}
func forwardAllSignals(containers execution.TasksClient, id string) chan os.Signal {
sigc := make(chan os.Signal, 128)
signal.Notify(sigc)
go func() {
for s := range sigc {
logrus.Debug("Forwarding signal ", s)
killRequest := &execution.KillRequest{
ContainerID: id,
Signal: uint32(s.(syscall.Signal)),
PidOrAll: &execution.KillRequest_All{
All: false,
},
}
_, err := containers.Kill(gocontext.Background(), killRequest)
if err != nil {
logrus.Fatalln(err)
}
}
}()
return sigc
}
func parseSignal(rawSignal string) (syscall.Signal, error) {
s, err := strconv.Atoi(rawSignal)
if err == nil {
sig := syscall.Signal(s)
for _, msig := range signalMap {
if sig == msig {
return sig, nil
}
}
return -1, fmt.Errorf("unknown signal %q", rawSignal)
}
signal, ok := signalMap[strings.TrimPrefix(strings.ToUpper(rawSignal), "SIG")]
if !ok {
return -1, fmt.Errorf("unknown signal %q", rawSignal)
}
return signal, nil
}
func stopCatch(sigc chan os.Signal) {
signal.Stop(sigc)
close(sigc)
}
// parseMountFlag parses a mount string in the form "type=foo,source=/path,destination=/target,options=rbind:rw"
func parseMountFlag(m string) (specs.Mount, error) {
mount := specs.Mount{}
r := csv.NewReader(strings.NewReader(m))
fields, err := r.Read()
if err != nil {
return mount, err
}
for _, field := range fields {
v := strings.Split(field, "=")
if len(v) != 2 {
return mount, fmt.Errorf("invalid mount specification: expected key=val")
}
key := v[0]
val := v[1]
switch key {
case "type":
mount.Type = val
case "source", "src":
mount.Source = val
case "destination", "dst":
mount.Destination = val
case "options":
mount.Options = strings.Split(val, ":")
default:
return mount, fmt.Errorf("mount option %q not supported", key)
}
}
return mount, nil
}
// replaceOrAppendEnvValues returns the defaults with the overrides either
// replaced by env key or appended to the list
func replaceOrAppendEnvValues(defaults, overrides []string) []string {
cache := make(map[string]int, len(defaults))
for i, e := range defaults {
parts := strings.SplitN(e, "=", 2)
cache[parts[0]] = i
}
for _, value := range overrides {
// Values w/o = means they want this env to be removed/unset.
if !strings.Contains(value, "=") {
if i, exists := cache[value]; exists {
defaults[i] = "" // Used to indicate it should be removed
}
continue
}
// Just do a normal set/update
parts := strings.SplitN(value, "=", 2)
if i, exists := cache[parts[0]]; exists {
defaults[i] = value
} else {
defaults = append(defaults, value)
}
}
// Now remove all entries that we want to "unset"
for i := 0; i < len(defaults); i++ {
if defaults[i] == "" {
defaults = append(defaults[:i], defaults[i+1:]...)
i--
}
}
return defaults
}