Beispiel #1
0
func (cli *HyperClient) hijackRequest(method, pod, tag string, v *url.Values) error {
	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")
		}
	}()

	request := fmt.Sprintf("/%s?%s", method, v.Encode())

	errCh = promise.Go(func() error {
		return cli.hijack("POST", request, true, cli.in, cli.out, cli.out, hijacked, nil, "")
	})

	if err := cli.monitorTtySize(pod, tag); err != nil {
		fmt.Printf("Monitor tty size fail for %s!\n", pod)
	}

	// 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", pod)
	return nil
}
Beispiel #2
0
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
	}
	return nil
}
Beispiel #3
0
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]
		tag         = cli.GetTag()
		containerId = podName
	)

	v := url.Values{}
	if strings.Contains(podName, "pod-") {
		_, err = cli.GetPodInfo(podName)
		if err != nil {
			return err
		}
		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, "")
	})

	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
}
Beispiel #4
0
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 Hyper daemon. Is 'hyperd' 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
}
Beispiel #5
0
// 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)"`
		Remove        bool     `long:"rm" default:"false" value-name:"" default-mask:"-" description:"Automatically remove the pod when it exits"`
	}

	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), opts.Remove)
		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
			userpod *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
		}
		userpod, err = kpod.Convert()
		if err != nil {
			return err
		}
		jsonbody, err = json.Marshal(*userpod)
		if err != nil {
			return err
		}
		t1 := time.Now()
		podId, err := cli.RunPod(string(jsonbody), opts.Remove)
		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) > 1 {
		command = args[2:]
	}
	if opts.Name == "" {
		fields := strings.Split(image, ":")
		if len(fields) < 2 {
			opts.Name = image + "-" + utils.RandStr(10, "number")
		} else {
			opts.Name = fields[0] + "-" + fields[1] + "-" + utils.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,
	}

	jsonString, _ := json.Marshal(userPod)
	podId, err := cli.RunPod(string(jsonString), opts.Remove)
	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
}