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 (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 }