client/lxd_containers.go
func (r *ProtocolLXD) ConsoleContainer(containerName string, console api.ContainerConsolePost, args *ContainerConsoleArgs) (Operation, error) {
	if !r.HasExtension("console") {
		return nil, fmt.Errorf("The server is missing the required \"console\" API extension")
	}

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

	if args == nil || args.Terminal == nil {
		return nil, fmt.Errorf("A terminal must be set")
	}

	if args.Control == nil {
		return nil, fmt.Errorf("A control channel must be set")
	}

	// 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)
		}
	}

	var controlConn *websocket.Conn
	// Call the control handler with a connection to the control socket
	if fds["control"] == "" {
		return nil, fmt.Errorf("Did not receive a file descriptor for the control channel")
	}

	controlConn, err = r.GetOperationWebsocket(opAPI.ID, fds["control"])
	if err != nil {
		return nil, err
	}

	go args.Control(controlConn)

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

	// Detach from console.
	go func(consoleDisconnect <-chan bool) {
		<-consoleDisconnect
		msg := websocket.FormatCloseMessage(websocket.CloseNormalClosure, "Detaching from console")
		// We don't care if this fails. This is just for convenience.
		controlConn.WriteMessage(websocket.CloseMessage, msg)
		controlConn.Close()
	}(args.ConsoleDisconnect)

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

	return op, nil
}
func (r *ProtocolLXD) ConsoleContainer(containerName string, console api.ContainerConsolePost, args *ContainerConsoleArgs) (Operation, error) {
	if !r.HasExtension("console") {
		return nil, fmt.Errorf("The server is missing the required \"console\" API extension")
	}

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

	if args == nil || args.Terminal == nil {
		return nil, fmt.Errorf("A terminal must be set")
	}

	if args.Control == nil {
		return nil, fmt.Errorf("A control channel must be set")
	}

	// 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)
		}
	}

	var controlConn *websocket.Conn
	// Call the control handler with a connection to the control socket
	if fds["control"] == "" {
		return nil, fmt.Errorf("Did not receive a file descriptor for the control channel")
	}

	controlConn, err = r.GetOperationWebsocket(opAPI.ID, fds["control"])
	if err != nil {
		return nil, err
	}

	go args.Control(controlConn)

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

	// Detach from console.
	go func(consoleDisconnect <-chan bool) {
		<-consoleDisconnect
		msg := websocket.FormatCloseMessage(websocket.CloseNormalClosure, "Detaching from console")
		// We don't care if this fails. This is just for convenience.
		controlConn.WriteMessage(websocket.CloseMessage, msg)
		controlConn.Close()
	}(args.ConsoleDisconnect)

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

	return op, nil
}
func ( r * ProtocolLXD ) ConsoleContainer ( containerName string , console api . ContainerConsolePost , args * ContainerConsoleArgs ) ( Operation , error ) { if ! r . HasExtension ( " " ) { return nil , fmt . Errorf ( " \" \" " ) } // Send the request op , _ , err := r . queryOperation ( " " , fmt . Sprintf ( " " , url . QueryEscape ( containerName ) ) , console , " " ) if err != nil { return nil , err } opAPI := op . Get ( ) if args == nil || args . Terminal == nil { return nil , fmt . Errorf ( " " ) } if args . Control == nil { return nil , fmt . Errorf ( " " ) } // 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 ) } } var controlConn * websocket . Conn // Call the control handler with a connection to the control socket if fds [ " " ] == " " { return nil , fmt . Errorf ( " " ) } controlConn , err = r . GetOperationWebsocket ( opAPI . ID , fds [ " " ] ) if err != nil { return nil , err } go args . Control ( controlConn ) // Connect to the websocket conn , err := r . GetOperationWebsocket ( opAPI . ID , fds [ " " ] ) if err != nil { return nil , err } // Detach from console. go func ( consoleDisconnect <- chan bool ) { <- consoleDisconnect msg := websocket . FormatCloseMessage ( websocket . CloseNormalClosure , " " ) // We don't care if this fails. This is just for convenience. controlConn . WriteMessage ( websocket . CloseMessage , msg ) controlConn . Close ( ) } ( args . ConsoleDisconnect ) // And attach stdin and stdout to it go func ( ) { shared . WebsocketSendStream ( conn , args . Terminal , - 1 ) <- shared . WebsocketRecvStream ( args . Terminal , conn ) conn . Close ( ) } ( ) return op , nil }
// ConsoleContainer requests that LXD attaches to the console device of a container.
ConsoleContainer requests that LXD attaches to the console device of a container .