client/lxd_containers.go
func (r *ProtocolLXD) ExecContainer(containerName string, exec api.ContainerExecPost, args *ContainerExecArgs) (Operation, error) {
	if exec.RecordOutput {
		if !r.HasExtension("container_exec_recording") {
			return nil, fmt.Errorf("The server is missing the required \"container_exec_recording\" API extension")
		}
	}

	// Send the request
	op, _, err := r.queryOperation("POST", fmt.Sprintf("/containers/%s/exec", url.QueryEscape(containerName)), exec, "")
	if err != nil {
		return nil, err
	}
	opAPI := op.Get()

	// Process additional arguments
	if args != nil {
		// Parse the fds
		fds := map[string]string{}

		value, ok := opAPI.Metadata["fds"]
		if ok {
			values := value.(map[string]interface{})
			for k, v := range values {
				fds[k] = v.(string)
			}
		}

		// Call the control handler with a connection to the control socket
		if args.Control != nil && fds["control"] != "" {
			conn, err := r.GetOperationWebsocket(opAPI.ID, fds["control"])
			if err != nil {
				return nil, err
			}

			go args.Control(conn)
		}

		if exec.Interactive {
			// Handle interactive sections
			if args.Stdin != nil && args.Stdout != nil {
				// Connect to the websocket
				conn, err := r.GetOperationWebsocket(opAPI.ID, fds["0"])
				if err != nil {
					return nil, err
				}

				// And attach stdin and stdout to it
				go func() {
					shared.WebsocketSendStream(conn, args.Stdin, -1)
					<-shared.WebsocketRecvStream(args.Stdout, conn)
					conn.Close()

					if args.DataDone != nil {
						close(args.DataDone)
					}
				}()
			} else {
				if args.DataDone != nil {
					close(args.DataDone)
				}
			}
		} else {
			// Handle non-interactive sessions
			dones := map[int]chan bool{}
			conns := []*websocket.Conn{}

			// Handle stdin
			if fds["0"] != "" {
				conn, err := r.GetOperationWebsocket(opAPI.ID, fds["0"])
				if err != nil {
					return nil, err
				}

				conns = append(conns, conn)
				dones[0] = shared.WebsocketSendStream(conn, args.Stdin, -1)
			}

			// Handle stdout
			if fds["1"] != "" {
				conn, err := r.GetOperationWebsocket(opAPI.ID, fds["1"])
				if err != nil {
					return nil, err
				}

				conns = append(conns, conn)
				dones[1] = shared.WebsocketRecvStream(args.Stdout, conn)
			}

			// Handle stderr
			if fds["2"] != "" {
				conn, err := r.GetOperationWebsocket(opAPI.ID, fds["2"])
				if err != nil {
					return nil, err
				}

				conns = append(conns, conn)
				dones[2] = shared.WebsocketRecvStream(args.Stderr, conn)
			}

			// Wait for everything to be done
			go func() {
				for i, chDone := range dones {
					// Skip stdin, dealing with it separately below
					if i == 0 {
						continue
					}

					<-chDone
				}

				if fds["0"] != "" {
					if args.Stdin != nil {
						args.Stdin.Close()
					}

					// Empty the stdin channel but don't block on it as
					// stdin may be stuck in Read()
					go func() {
						<-dones[0]
					}()
				}

				for _, conn := range conns {
					conn.Close()
				}

				if args.DataDone != nil {
					close(args.DataDone)
				}
			}()
		}
	}

	return op, nil
}
func (r *ProtocolLXD) ExecContainer(containerName string, exec api.ContainerExecPost, args *ContainerExecArgs) (Operation, error) {
	if exec.RecordOutput {
		if !r.HasExtension("container_exec_recording") {
			return nil, fmt.Errorf("The server is missing the required \"container_exec_recording\" API extension")
		}
	}

	// Send the request
	op, _, err := r.queryOperation("POST", fmt.Sprintf("/containers/%s/exec", url.QueryEscape(containerName)), exec, "")
	if err != nil {
		return nil, err
	}
	opAPI := op.Get()

	// Process additional arguments
	if args != nil {
		// Parse the fds
		fds := map[string]string{}

		value, ok := opAPI.Metadata["fds"]
		if ok {
			values := value.(map[string]interface{})
			for k, v := range values {
				fds[k] = v.(string)
			}
		}

		// Call the control handler with a connection to the control socket
		if args.Control != nil && fds["control"] != "" {
			conn, err := r.GetOperationWebsocket(opAPI.ID, fds["control"])
			if err != nil {
				return nil, err
			}

			go args.Control(conn)
		}

		if exec.Interactive {
			// Handle interactive sections
			if args.Stdin != nil && args.Stdout != nil {
				// Connect to the websocket
				conn, err := r.GetOperationWebsocket(opAPI.ID, fds["0"])
				if err != nil {
					return nil, err
				}

				// And attach stdin and stdout to it
				go func() {
					shared.WebsocketSendStream(conn, args.Stdin, -1)
					<-shared.WebsocketRecvStream(args.Stdout, conn)
					conn.Close()

					if args.DataDone != nil {
						close(args.DataDone)
					}
				}()
			} else {
				if args.DataDone != nil {
					close(args.DataDone)
				}
			}
		} else {
			// Handle non-interactive sessions
			dones := map[int]chan bool{}
			conns := []*websocket.Conn{}

			// Handle stdin
			if fds["0"] != "" {
				conn, err := r.GetOperationWebsocket(opAPI.ID, fds["0"])
				if err != nil {
					return nil, err
				}

				conns = append(conns, conn)
				dones[0] = shared.WebsocketSendStream(conn, args.Stdin, -1)
			}

			// Handle stdout
			if fds["1"] != "" {
				conn, err := r.GetOperationWebsocket(opAPI.ID, fds["1"])
				if err != nil {
					return nil, err
				}

				conns = append(conns, conn)
				dones[1] = shared.WebsocketRecvStream(args.Stdout, conn)
			}

			// Handle stderr
			if fds["2"] != "" {
				conn, err := r.GetOperationWebsocket(opAPI.ID, fds["2"])
				if err != nil {
					return nil, err
				}

				conns = append(conns, conn)
				dones[2] = shared.WebsocketRecvStream(args.Stderr, conn)
			}

			// Wait for everything to be done
			go func() {
				for i, chDone := range dones {
					// Skip stdin, dealing with it separately below
					if i == 0 {
						continue
					}

					<-chDone
				}

				if fds["0"] != "" {
					if args.Stdin != nil {
						args.Stdin.Close()
					}

					// Empty the stdin channel but don't block on it as
					// stdin may be stuck in Read()
					go func() {
						<-dones[0]
					}()
				}

				for _, conn := range conns {
					conn.Close()
				}

				if args.DataDone != nil {
					close(args.DataDone)
				}
			}()
		}
	}

	return op, nil
}
func ( r * ProtocolLXD ) ExecContainer ( containerName string , exec api . ContainerExecPost , args * ContainerExecArgs ) ( Operation , error ) { if exec . RecordOutput { if ! r . HasExtension ( " " ) { return nil , fmt . Errorf ( " \" \" " ) } } // Send the request op , _ , err := r . queryOperation ( " " , fmt . Sprintf ( " " , url . QueryEscape ( containerName ) ) , exec , " " ) if err != nil { return nil , err } opAPI := op . Get ( ) // Process additional arguments if args != nil { // Parse the fds fds := map [ string ] string { } value , ok := opAPI . Metadata [ " " ] if ok { values := value . ( map [ string ] interface { } ) for k , v := range values { fds [ k ] = v . ( string ) } } // Call the control handler with a connection to the control socket if args . Control != nil && fds [ " " ] != " " { conn , err := r . GetOperationWebsocket ( opAPI . ID , fds [ " " ] ) if err != nil { return nil , err } go args . Control ( conn ) } if exec . Interactive { // Handle interactive sections if args . Stdin != nil && args . Stdout != nil { // Connect to the websocket conn , err := r . GetOperationWebsocket ( opAPI . ID , fds [ " " ] ) if err != nil { return nil , err } // And attach stdin and stdout to it go func ( ) { shared . WebsocketSendStream ( conn , args . Stdin , - 1 ) <- shared . WebsocketRecvStream ( args . Stdout , conn ) conn . Close ( ) if args . DataDone != nil { close ( args . DataDone ) } } ( ) } else { if args . DataDone != nil { close ( args . DataDone ) } } } else { // Handle non-interactive sessions dones := map [ int ] chan bool { } conns := [ ] * websocket . Conn { } // Handle stdin if fds [ " " ] != " " { conn , err := r . GetOperationWebsocket ( opAPI . ID , fds [ " " ] ) if err != nil { return nil , err } conns = append ( conns , conn ) dones [ 0 ] = shared . WebsocketSendStream ( conn , args . Stdin , - 1 ) } // Handle stdout if fds [ " " ] != " " { conn , err := r . GetOperationWebsocket ( opAPI . ID , fds [ " " ] ) if err != nil { return nil , err } conns = append ( conns , conn ) dones [ 1 ] = shared . WebsocketRecvStream ( args . Stdout , conn ) } // Handle stderr if fds [ " " ] != " " { conn , err := r . GetOperationWebsocket ( opAPI . ID , fds [ " " ] ) if err != nil { return nil , err } conns = append ( conns , conn ) dones [ 2 ] = shared . WebsocketRecvStream ( args . Stderr , conn ) } // Wait for everything to be done go func ( ) { for i , chDone := range dones { // Skip stdin, dealing with it separately below if i == 0 { continue } <- chDone } if fds [ " " ] != " " { if args . Stdin != nil { args . Stdin . Close ( ) } // Empty the stdin channel but don't block on it as // stdin may be stuck in Read() go func ( ) { <- dones [ 0 ] } ( ) } for _ , conn := range conns { conn . Close ( ) } if args . DataDone != nil { close ( args . DataDone ) } } ( ) } } return op , nil }
// ExecContainer requests that LXD spawns a command inside the container
ExecContainer requests that LXD spawns a command inside the container