1
0
mirror of https://github.com/rancher/cli.git synced 2026-02-05 09:48:36 +01:00
Files
cli/cmd/exec.go
Daishan Peng 03ebe1a203 cli 2.0
2017-09-15 14:34:17 -07:00

195 lines
4.5 KiB
Go

package cmd
import (
"bufio"
"fmt"
"os"
"os/exec"
"strconv"
"strings"
"github.com/rancher/go-rancher/v3"
"github.com/urfave/cli"
)
func ExecCommand() cli.Command {
return cli.Command{
Name: "exec",
Usage: "Run a command on a container",
Description: "\nThe command will find the container on the host and use `docker exec` to access the container. Any options that `docker exec` uses can be passed as an option for `rancher exec`.\n\nExample:\n\t$ rancher exec -i -t 1i1\n",
Action: execCommand,
SkipFlagParsing: true,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "help-docker",
Usage: "Display the 'docker exec --help'",
},
},
}
}
func execCommand(ctx *cli.Context) error {
return processExitCode(execCommandInternal(ctx))
}
func execCommandInternal(ctx *cli.Context) error {
args := ctx.Args()
if len(args) > 0 && (args[0] == "-h" || args[0] == "--help") {
return cli.ShowCommandHelp(ctx, "exec")
}
if len(args) > 0 && args[0] == "--help-docker" {
return runDockerHelp("exec")
}
c, err := GetClient(ctx)
if err != nil {
return err
}
args, hostID, _, err := selectContainer(c, ctx.Args())
if err != nil {
return err
}
// this is a massive hack. Need to fix the real issue
args = append([]string{"-i"}, args...)
return runDockerCommand(hostID, c, "exec", args)
}
func isHelp(args []string) bool {
for _, i := range args {
if i == "--help" || i == "-h" {
return true
}
}
return false
}
func selectContainer(c *client.RancherClient, args []string) ([]string, string, string, error) {
newArgs := make([]string, len(args))
copy(newArgs, args)
name := ""
index := 0
for i, val := range newArgs {
if !strings.HasPrefix(val, "-") {
name = val
index = i
break
}
}
if name == "" {
return nil, "", "", fmt.Errorf("Please specify container name as an argument")
}
resource, err := Lookup(c, name, "container", "service")
if err != nil {
return nil, "", "", err
}
if _, ok := resource.Links["host"]; ok {
hostID, containerID, err := getHostnameAndContainerID(c, resource.Id)
if err != nil {
return nil, "", "", err
}
newArgs[index] = containerID
return newArgs, hostID, containerID, nil
}
if _, ok := resource.Links["instances"]; ok {
var instances client.ContainerCollection
if err := c.GetLink(*resource, "instances", &instances); err != nil {
return nil, "", "", err
}
hostID, containerID, err := getHostnameAndContainerIDFromList(c, instances)
if err != nil {
return nil, "", "", err
}
newArgs[index] = containerID
return newArgs, hostID, containerID, nil
}
return nil, "", "", nil
}
func getHostnameAndContainerIDFromList(c *client.RancherClient, containers client.ContainerCollection) (string, string, error) {
if len(containers.Data) == 0 {
return "", "", fmt.Errorf("Failed to find a container")
}
if len(containers.Data) == 1 {
return containers.Data[0].HostId, containers.Data[0].ExternalId, nil
}
names := []string{}
for _, container := range containers.Data {
name := ""
if container.Name == "" {
name = container.Id
} else {
name = container.Name
}
names = append(names, fmt.Sprintf("%s (%s)", name, container.PrimaryIpAddress))
}
index := selectFromList("Containers:", names)
return containers.Data[index].HostId, containers.Data[index].ExternalId, nil
}
func selectFromList(header string, choices []string) int {
if header != "" {
fmt.Println(header)
}
reader := bufio.NewReader(os.Stdin)
selected := -1
for selected <= 0 || selected > len(choices) {
for i, choice := range choices {
fmt.Printf("[%d] %s\n", i+1, choice)
}
fmt.Print("Select: ")
text, _ := reader.ReadString('\n')
text = strings.TrimSpace(text)
num, err := strconv.Atoi(text)
if err == nil {
selected = num
}
}
return selected - 1
}
func getHostnameAndContainerID(c *client.RancherClient, containerID string) (string, string, error) {
container, err := c.Container.ById(containerID)
if err != nil {
return "", "", err
}
var host client.Host
if err := c.GetLink(container.Resource, "host", &host); err != nil {
return "", "", err
}
if host.Id == "" {
return "", "", fmt.Errorf("Failed to find host for container %s", container.Name)
}
return host.Id, container.ExternalId, nil
}
func runDockerHelp(subcommand string) error {
args := []string{"--help"}
if subcommand != "" {
args = []string{subcommand, "--help"}
}
cmd := exec.Command("docker", args...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}