// hyper run [OPTIONS] image [COMMAND] [ARGS...] func (cli *HyperClient) HyperCmdRun(args ...string) error { if len(args) == 0 { return fmt.Errorf("%s ERROR: Can not accept the 'run' command without argument!\n", os.Args[0]) } var opts struct { PodFile string `short:"p" long:"podfile" value-name:"\"\"" description:"Create and Run a pod based on the pod file"` K8s string `short:"k" long:"kubernetes" value-name:"\"\"" description:"Create and Run a pod based on the kubernetes pod file"` Yaml bool `short:"y" long:"yaml" default:"false" default-mask:"-" description:"Create a pod based on Yaml file"` Name string `long:"name" value-name:"\"\"" description:"Assign a name to the container"` Attach bool `long:"attach" default:"true" default-mask:"-" description:"Attach the stdin, stdout and stderr to the container"` Workdir string `long:"workdir" default:"/" value-name:"\"\"" default-mask:"-" description:"Working directory inside the container"` Tty bool `long:"tty" default:"true" default-mask:"-" description:"Allocate a pseudo-TTY"` Cpu int `long:"cpu" default:"1" value-name:"1" default-mask:"-" description:"CPU number for the VM"` Memory int `long:"memory" default:"128" value-name:"128" default-mask:"-" description:"Memory size (MB) for the VM"` Env []string `long:"env" value-name:"[]" default-mask:"-" description:"Set environment variables"` EntryPoint string `long:"entrypoint" value-name:"\"\"" default-mask:"-" description:"Overwrite the default ENTRYPOINT of the image"` RestartPolicy string `long:"restart" default:"never" value-name:"\"\"" default-mask:"-" description:"Restart policy to apply when a container exits (never, onFailure, always)"` } var parser = gflag.NewParser(&opts, gflag.Default|gflag.IgnoreUnknown) parser.Usage = "run [OPTIONS] IMAGE [COMMAND] [ARG...]\n\ncreate a pod, and launch a new VM to run the pod" args, err := parser.Parse() if err != nil { if !strings.Contains(err.Error(), "Usage") { return err } else { return nil } } if opts.PodFile != "" { if _, err := os.Stat(opts.PodFile); err != nil { return err } jsonbody, err := ioutil.ReadFile(opts.PodFile) if err != nil { return err } if opts.Yaml == true { jsonbody, err = cli.ConvertYamlToJson(jsonbody) if err != nil { return err } } t1 := time.Now() podId, err := cli.RunPod(string(jsonbody)) if err != nil { return err } fmt.Printf("POD id is %s\n", podId) t2 := time.Now() fmt.Printf("Time to run a POD is %d ms\n", (t2.UnixNano()-t1.UnixNano())/1000000) return nil } if opts.K8s != "" { var ( kpod pod.KPod pod *pod.UserPod ) if _, err := os.Stat(opts.K8s); err != nil { return err } jsonbody, err := ioutil.ReadFile(opts.K8s) if err != nil { return err } if opts.Yaml == true { jsonbody, err = cli.ConvertYamlToJson(jsonbody) if err != nil { return err } } if err := json.Unmarshal(jsonbody, &kpod); err != nil { return err } pod, err = kpod.Convert() if err != nil { return err } jsonbody, err = json.Marshal(*pod) if err != nil { return err } t1 := time.Now() podId, err := cli.RunPod(string(jsonbody)) if err != nil { return err } fmt.Printf("POD id is %s\n", podId) t2 := time.Now() fmt.Printf("Time to run a POD is %d ms\n", (t2.UnixNano()-t1.UnixNano())/1000000) return nil } if len(args) == 0 { return fmt.Errorf("%s: \"run\" requires a minimum of 1 argument, please provide the image.", os.Args[0]) } var ( image = args[1] command = []string{} env = []pod.UserEnvironmentVar{} ) if len(args) > 2 { command = args[2:] } if opts.Name == "" { opts.Name = image + "-" + pod.RandStr(10, "number") } if opts.Memory == 0 { opts.Memory = 128 } if opts.Cpu == 0 { opts.Cpu = 1 } for _, v := range opts.Env { if v == "" || !strings.Contains(v, "=") { continue } userEnv := pod.UserEnvironmentVar{ Env: v[:strings.Index(v, "=")], Value: v[strings.Index(v, "=")+1:], } env = append(env, userEnv) } var containerList = []pod.UserContainer{} var container = pod.UserContainer{ Name: opts.Name, Image: image, Command: command, Workdir: opts.Workdir, Entrypoint: []string{}, Ports: []pod.UserContainerPort{}, Envs: env, Volumes: []pod.UserVolumeReference{}, Files: []pod.UserFileReference{}, RestartPolicy: opts.RestartPolicy, } containerList = append(containerList, container) var userPod = &pod.UserPod{ Name: opts.Name, Containers: containerList, Resource: pod.UserResource{Vcpu: opts.Cpu, Memory: opts.Memory}, Files: []pod.UserFile{}, Volumes: []pod.UserVolume{}, Tty: opts.Tty, } /* if err := userPod.Validate(); err != nil { return err } */ jsonString, _ := json.Marshal(userPod) podId, err := cli.RunPod(string(jsonString)) if err != nil { return err } fmt.Printf("POD id is %s\n", podId) // Get the container ID of this POD containerId, err := cli.GetContainerByPod(podId) if err != nil { return err } var ( tag = cli.GetTag() hijacked = make(chan io.Closer) errCh chan error ) v := url.Values{} v.Set("type", "container") v.Set("value", containerId) v.Set("tag", tag) // Block the return until the chan gets closed defer func() { // fmt.Printf("End of CmdExec(), Waiting for hijack to finish.\n") if _, ok := <-hijacked; ok { fmt.Printf("Hijack did not finish (chan still open)\n") } }() errCh = promise.Go(func() error { return cli.hijack("POST", "/attach?"+v.Encode(), true, cli.in, cli.out, cli.out, hijacked, nil, "") }) if err := cli.monitorTtySize(podId, tag); err != nil { fmt.Printf("Monitor tty size fail for %s!\n", podId) } // Acknowledge the hijack before starting select { case closer := <-hijacked: // Make sure that hijack gets closed when returning. (result // in closing hijack chan and freeing server's goroutines. if closer != nil { defer closer.Close() } case err := <-errCh: if err != nil { fmt.Printf("Error hijack: %s", err.Error()) return err } } if err := <-errCh; err != nil { fmt.Printf("Error hijack: %s", err.Error()) return err } // fmt.Printf("Success to exec the command %s for POD %s!\n", command, podId) return nil }
func (cli *HyperClient) HyperCmdAttach(args ...string) error { var parser = gflag.NewParser(nil, gflag.Default) parser.Usage = "attach CONTAINER\n\nattach to the tty of a specified container in a pod" args, err := parser.Parse() if err != nil { if !strings.Contains(err.Error(), "Usage") { return err } else { return nil } } if len(args) == 1 { return fmt.Errorf("Can not accept the 'attach' command without Container ID!") } var ( podName = args[1] hostname = "" tag = cli.GetTag() containerId = podName ) v := url.Values{} if strings.Contains(podName, "pod-") { hostname, err = cli.GetPodInfo(podName) if err != nil { return err } if hostname == "" { return fmt.Errorf("The pod(%s) does not exist, please create it before exec!", podName) } containerId, err = cli.GetContainerByPod(podName) if err != nil { return err } v.Set("type", "container") v.Set("value", containerId) } else { v.Set("type", "container") v.Set("value", containerId) } v.Set("tag", tag) var ( hijacked = make(chan io.Closer) errCh chan error ) // Block the return until the chan gets closed defer func() { fmt.Printf("End of CmdExec(), Waiting for hijack to finish.\n") if _, ok := <-hijacked; ok { fmt.Printf("Hijack did not finish (chan still open)\n") } }() errCh = promise.Go(func() error { return cli.hijack("POST", "/attach?"+v.Encode(), true, cli.in, cli.out, cli.out, hijacked, nil, hostname) }) if err := cli.monitorTtySize(podName, tag); err != nil { fmt.Printf("Monitor tty size fail for %s!\n", podName) } // Acknowledge the hijack before starting select { case closer := <-hijacked: // Make sure that hijack gets closed when returning. (result // in closing hijack chan and freeing server's goroutines. if closer != nil { defer closer.Close() } case err := <-errCh: if err != nil { fmt.Printf("Error hijack: %s", err.Error()) return err } } if err := <-errCh; err != nil { fmt.Printf("Error hijack: %s", err.Error()) return err } fmt.Printf("Successfully attached to pod(%s)\n", podName) return nil }
func (cli *HyperClient) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer, started chan io.Closer, data interface{}, hostname string) error { defer func() { if started != nil { close(started) } }() params, err := cli.encodeData(data) if err != nil { return err } req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", utils.APIVERSION, path), params) if err != nil { return err } req.Header.Set("User-Agent", "Hyper-Client/"+utils.VERSION) req.Header.Set("Content-Type", "text/plain") req.Header.Set("Connection", "Upgrade") req.Header.Set("Upgrade", "tcp") req.Host = cli.addr dial, err := cli.dial() if err != nil { return err } // When we set up a TCP connection for hijack, there could be long periods // of inactivity (a long running command with no output) that in certain // network setups may cause ECONNTIMEOUT, leaving the client in an unknown // state. Setting TCP KeepAlive on the socket connection will prohibit // ECONNTIMEOUT unless the socket connection truly is broken if tcpConn, ok := dial.(*net.TCPConn); ok { tcpConn.SetKeepAlive(true) tcpConn.SetKeepAlivePeriod(30 * time.Second) } if err != nil { if strings.Contains(err.Error(), "connection refused") { return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker' running on this host?") } return err } clientconn := httputil.NewClientConn(dial, nil) defer clientconn.Close() // Server hijacks the connection, error 'connection closed' expected _, err = clientconn.Do(req) if err != nil { fmt.Printf("Client DO: %s\n", err.Error()) } rwc, br := clientconn.Hijack() defer rwc.Close() if started != nil { started <- rwc } var ( receiveStdout chan error oldState *term.State ) if in != nil && setRawTerminal { // fmt.Printf("In the Raw Terminal!!!\n") oldState, err = term.SetRawTerminal(cli.inFd) if err != nil { return err } defer term.RestoreTerminal(cli.inFd, oldState) } if stdout != nil || stderr != nil { receiveStdout = promise.Go(func() (err error) { defer func() { if in != nil { if setRawTerminal { term.RestoreTerminal(cli.inFd, oldState) } } }() _, err = io.Copy(stdout, br) // fmt.Printf("[hijack] End of stdout\n") return err }) } sendStdin := promise.Go(func() error { if in != nil { io.Copy(rwc, in) // fmt.Printf("[hijack] End of stdin\n") } if conn, ok := rwc.(interface { CloseWrite() error }); ok { if err := conn.CloseWrite(); err != nil { fmt.Printf("Couldn't send EOF: %s", err.Error()) } } // Discard errors due to pipe interruption return nil }) if stdout != nil || stderr != nil { if err := <-receiveStdout; err != nil { fmt.Printf("Error receiveStdout: %s\n", err.Error()) return err } sendStdin <- nil } if in != nil { if err := <-sendStdin; err != nil { fmt.Printf("Error sendStdin: %s\n", err.Error()) return err } } return nil }
func (cli *HyperClient) HyperCmdExec(args ...string) error { var opts struct { Attach bool `short:"a" long:"attach" default:"true" value-name:"false" description:"attach current terminal to the stdio of command"` Vm bool `long:"vm" default:"false" value-name:"false" description:"attach to vm"` } var parser = gflag.NewParser(&opts, gflag.Default|gflag.IgnoreUnknown) parser.Usage = "exec [OPTIONS] POD|CONTAINER COMMAND [ARGS...]\n\nrun a command in a container of a running pod" args, err := parser.Parse() if err != nil { if !strings.Contains(err.Error(), "Usage") { return err } else { return nil } } if len(args) == 1 { return fmt.Errorf("Can not accept the 'exec' command without POD/Container ID!") } if len(args) == 2 { return fmt.Errorf("Can not accept the 'exec' command without command!") } var ( podName = args[1] hostname = "" tag = cli.GetTag() containerId string ) command, err := json.Marshal(args[2:]) if err != nil { return err } // fmt.Printf("The pod name is %s, command is %s\n", podName, string(command)) v := url.Values{} if opts.Vm == true { v.Set("type", "pod") v.Set("value", podName) } else { if strings.Contains(podName, "pod-") { hostname, err = cli.GetPodInfo(podName) if err != nil { return err } if hostname == "" { return fmt.Errorf("The POD : %s does not exist, please create it before exec!", podName) } containerId, err = cli.GetContainerByPod(podName) if err != nil { return err } v.Set("type", "container") v.Set("value", containerId) } else { v.Set("type", "container") v.Set("value", podName) } } v.Set("command", string(command)) v.Set("tag", tag) var ( hijacked = make(chan io.Closer) errCh chan error ) // Block the return until the chan gets closed defer func() { //fmt.Printf("End of CmdExec(), Waiting for hijack to finish.\n") if _, ok := <-hijacked; ok { fmt.Printf("Hijack did not finish (chan still open)\n") } }() errCh = promise.Go(func() error { return cli.hijack("POST", "/exec?"+v.Encode(), true, cli.in, cli.out, cli.out, hijacked, nil, hostname) }) if err := cli.monitorTtySize(podName, tag); err != nil { fmt.Printf("Monitor tty size fail for %s!\n", podName) } // Acknowledge the hijack before starting select { case closer := <-hijacked: // Make sure that hijack gets closed when returning. (result // in closing hijack chan and freeing server's goroutines. if closer != nil { defer closer.Close() } case err := <-errCh: if err != nil { fmt.Printf("Error hijack: %s", err.Error()) return err } } if err := <-errCh; err != nil { fmt.Printf("Error hijack: %s", err.Error()) return err } //fmt.Printf("Success to exec the command %s for POD %s!\n", command, podName) return nil }