func (s *rpcServer) Enter(inStream pb.Kurma_EnterServer) error { s.log.Debug("Received enter request") // read the first chunk to make sure its real and get the container ID chunk, err := inStream.Recv() if err != nil { return err } // create the outbound stream outStream, err := s.client.Enter(inStream.Context()) if err != nil { return err } // Create our inbound streams inWriter := pb.NewByteStreamWriter(inStream, chunk.StreamId) inReader := pb.NewByteStreamReader(inStream, nil) // Create our outbound streams outWriter := pb.NewByteStreamWriter(outStream, chunk.StreamId) outReader := pb.NewByteStreamReader(outStream, nil) // write the first byte to the backend so it is initialized if _, err := outWriter.Write(nil); err != nil { return err } // stream between! go io.Copy(outWriter, inReader) io.Copy(inWriter, outReader) outStream.CloseSend() return nil }
func enter(cmd *cli.Cmd) error { // Set the local terminal in raw mode to turn off buffering and local // echo. Also defers setting it back to normal for when the call is done. termios, err := raw.MakeRaw(os.Stdin.Fd()) if err != nil { return err } defer raw.TcSetAttr(os.Stdin.Fd(), termios) // Initialize the call and send the first packet so that it knows what // container we're connecting to. stream, err := cmd.Client.Enter(context.Background()) if err != nil { return err } w := pb.NewByteStreamWriter(stream, cmd.Args[0]) r := pb.NewByteStreamReader(stream, nil) if _, err := w.Write(nil); err != nil { return err } go io.Copy(w, os.Stdin) io.Copy(os.Stdout, r) stream.CloseSend() return nil }
func (s *rpcServer) UploadImage(stream pb.Kurma_UploadImageServer) error { s.log.Debug("Received upload request") packet, err := stream.Recv() if err != nil { return err } pc := s.pendingUploads[packet.StreamId] delete(s.pendingUploads, packet.StreamId) r := pb.NewByteStreamReader(stream, packet) s.log.Debug("Initializing container") _, err = s.manager.Create(pc.name, pc.imageManifest, r) return err }
func (s *rpcServer) Enter(stream pb.Kurma_EnterServer) error { s.log.Debug("Received enter request") // Receive the first chunk so we can get the stream ID, which will be the UUID // of the container. The byte portion will be blank, the client always sends a // chunk first so the UUID is available immediately. chunk, err := stream.Recv() if err != nil { return err } // get the container container := s.manager.Container(chunk.StreamId) if container == nil { return fmt.Errorf("specified container not found") } // configure the io.Reader/Writer for the transport w := pb.NewByteStreamWriter(stream, chunk.StreamId) r := pb.NewByteStreamReader(stream, nil) // create a pty, which we'll use for the process entering the container and // copy the data back up the transport. master, slave, err := pty.Open() if err != nil { return err } defer func() { slave.Close() master.Close() }() go io.Copy(w, master) go io.Copy(master, r) // enter into the container if err := container.Enter(slave); err != nil { return err } s.log.Debugf("Enter request finished") return nil }
func (s *rpcServer) UploadImage(inStream pb.Kurma_UploadImageServer) error { s.log.Debug("Received upload request") // NOTE: Unlike in the local daemon, we don't need to revalidate the image // manifest between Create and Upload. The backend validates in the Manager // because within the backend, there are multiple inputs to Manager.Create // (ie, bootstrapping vs the local API). // // Over the local API, it will cache the image manifest from the Create call // and re-use it on the UploadImage call, not pulling it from the binary // image. packet, err := inStream.Recv() if err != nil { return err } outStream, err := s.client.UploadImage(inStream.Context()) if err != nil { return err } r := pb.NewByteStreamReader(inStream, packet) w := pb.NewByteStreamWriter(outStream, packet.StreamId) if _, err := io.Copy(w, r); err != nil { return fmt.Errorf("write error: %v", err) } if _, err := outStream.CloseAndRecv(); err != nil { return err } if err := inStream.SendAndClose(&pb.None{}); err != nil { return err } return nil }