diff --git a/api/services/control/control.pb.go b/api/services/control/control.pb.go index 45ca3194e..c0862fcc0 100644 --- a/api/services/control/control.pb.go +++ b/api/services/control/control.pb.go @@ -19,6 +19,7 @@ Vertex VertexStatus VertexLog + BytesMessage */ package moby_buildkit_v1 @@ -121,6 +122,7 @@ type SolveRequest struct { Definition [][]byte `protobuf:"bytes,2,rep,name=Definition" json:"Definition,omitempty"` Exporter string `protobuf:"bytes,3,opt,name=Exporter,proto3" json:"Exporter,omitempty"` ExporterAttrs map[string]string `protobuf:"bytes,4,rep,name=ExporterAttrs" json:"ExporterAttrs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Session string `protobuf:"bytes,5,opt,name=Session,proto3" json:"Session,omitempty"` } func (m *SolveRequest) Reset() { *m = SolveRequest{} } @@ -156,6 +158,13 @@ func (m *SolveRequest) GetExporterAttrs() map[string]string { return nil } +func (m *SolveRequest) GetSession() string { + if m != nil { + return m.Session + } + return "" +} + type SolveResponse struct { Vtx []*Vertex `protobuf:"bytes,1,rep,name=vtx" json:"vtx,omitempty"` } @@ -369,6 +378,22 @@ func (m *VertexLog) GetMsg() []byte { return nil } +type BytesMessage struct { + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` +} + +func (m *BytesMessage) Reset() { *m = BytesMessage{} } +func (m *BytesMessage) String() string { return proto.CompactTextString(m) } +func (*BytesMessage) ProtoMessage() {} +func (*BytesMessage) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{10} } + +func (m *BytesMessage) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + func init() { proto.RegisterType((*DiskUsageRequest)(nil), "moby.buildkit.v1.DiskUsageRequest") proto.RegisterType((*DiskUsageResponse)(nil), "moby.buildkit.v1.DiskUsageResponse") @@ -380,6 +405,7 @@ func init() { proto.RegisterType((*Vertex)(nil), "moby.buildkit.v1.Vertex") proto.RegisterType((*VertexStatus)(nil), "moby.buildkit.v1.VertexStatus") proto.RegisterType((*VertexLog)(nil), "moby.buildkit.v1.VertexLog") + proto.RegisterType((*BytesMessage)(nil), "moby.buildkit.v1.BytesMessage") } // Reference imports to suppress errors if they are not otherwise used. @@ -396,6 +422,7 @@ type ControlClient interface { DiskUsage(ctx context.Context, in *DiskUsageRequest, opts ...grpc.CallOption) (*DiskUsageResponse, error) Solve(ctx context.Context, in *SolveRequest, opts ...grpc.CallOption) (*SolveResponse, error) Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (Control_StatusClient, error) + Session(ctx context.Context, opts ...grpc.CallOption) (Control_SessionClient, error) } type controlClient struct { @@ -456,12 +483,44 @@ func (x *controlStatusClient) Recv() (*StatusResponse, error) { return m, nil } +func (c *controlClient) Session(ctx context.Context, opts ...grpc.CallOption) (Control_SessionClient, error) { + stream, err := grpc.NewClientStream(ctx, &_Control_serviceDesc.Streams[1], c.cc, "/moby.buildkit.v1.Control/Session", opts...) + if err != nil { + return nil, err + } + x := &controlSessionClient{stream} + return x, nil +} + +type Control_SessionClient interface { + Send(*BytesMessage) error + Recv() (*BytesMessage, error) + grpc.ClientStream +} + +type controlSessionClient struct { + grpc.ClientStream +} + +func (x *controlSessionClient) Send(m *BytesMessage) error { + return x.ClientStream.SendMsg(m) +} + +func (x *controlSessionClient) Recv() (*BytesMessage, error) { + m := new(BytesMessage) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + // Server API for Control service type ControlServer interface { DiskUsage(context.Context, *DiskUsageRequest) (*DiskUsageResponse, error) Solve(context.Context, *SolveRequest) (*SolveResponse, error) Status(*StatusRequest, Control_StatusServer) error + Session(Control_SessionServer) error } func RegisterControlServer(s *grpc.Server, srv ControlServer) { @@ -525,6 +584,32 @@ func (x *controlStatusServer) Send(m *StatusResponse) error { return x.ServerStream.SendMsg(m) } +func _Control_Session_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(ControlServer).Session(&controlSessionServer{stream}) +} + +type Control_SessionServer interface { + Send(*BytesMessage) error + Recv() (*BytesMessage, error) + grpc.ServerStream +} + +type controlSessionServer struct { + grpc.ServerStream +} + +func (x *controlSessionServer) Send(m *BytesMessage) error { + return x.ServerStream.SendMsg(m) +} + +func (x *controlSessionServer) Recv() (*BytesMessage, error) { + m := new(BytesMessage) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + var _Control_serviceDesc = grpc.ServiceDesc{ ServiceName: "moby.buildkit.v1.Control", HandlerType: (*ControlServer)(nil), @@ -544,6 +629,12 @@ var _Control_serviceDesc = grpc.ServiceDesc{ Handler: _Control_Status_Handler, ServerStreams: true, }, + { + StreamName: "Session", + Handler: _Control_Session_Handler, + ServerStreams: true, + ClientStreams: true, + }, }, Metadata: "control.proto", } @@ -697,6 +788,12 @@ func (m *SolveRequest) MarshalTo(dAtA []byte) (int, error) { i += copy(dAtA[i:], v) } } + if len(m.Session) > 0 { + dAtA[i] = 0x2a + i++ + i = encodeVarintControl(dAtA, i, uint64(len(m.Session))) + i += copy(dAtA[i:], m.Session) + } return i, nil } @@ -1006,6 +1103,30 @@ func (m *VertexLog) MarshalTo(dAtA []byte) (int, error) { return i, nil } +func (m *BytesMessage) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BytesMessage) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Data) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintControl(dAtA, i, uint64(len(m.Data))) + i += copy(dAtA[i:], m.Data) + } + return i, nil +} + func encodeFixed64Control(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) dAtA[offset+1] = uint8(v >> 8) @@ -1095,6 +1216,10 @@ func (m *SolveRequest) Size() (n int) { n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) } } + l = len(m.Session) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } return n } @@ -1232,6 +1357,16 @@ func (m *VertexLog) Size() (n int) { return n } +func (m *BytesMessage) Size() (n int) { + var l int + _ = l + l = len(m.Data) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + func sovControl(x uint64) (n int) { for { n++ @@ -1746,6 +1881,35 @@ func (m *SolveRequest) Unmarshal(dAtA []byte) error { m.ExporterAttrs[mapkey] = mapvalue } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Session", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Session = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipControl(dAtA[iNdEx:]) @@ -2752,6 +2916,87 @@ func (m *VertexLog) Unmarshal(dAtA []byte) error { } return nil } +func (m *BytesMessage) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BytesMessage: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BytesMessage: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipControl(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 @@ -2860,55 +3105,57 @@ var ( func init() { proto.RegisterFile("control.proto", fileDescriptorControl) } var fileDescriptorControl = []byte{ - // 785 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xa4, 0x55, 0x4f, 0x6f, 0xe3, 0x44, - 0x14, 0x67, 0xec, 0xd4, 0x49, 0x5e, 0x92, 0x55, 0x19, 0xa1, 0x95, 0x65, 0x44, 0x12, 0xcc, 0x25, - 0x5a, 0x69, 0x1d, 0x36, 0x80, 0x84, 0x8a, 0x84, 0x20, 0x64, 0x25, 0x5a, 0xb1, 0x97, 0xe9, 0x16, - 0xce, 0x4e, 0x32, 0x75, 0xad, 0xd8, 0x9e, 0x30, 0x33, 0x8e, 0x1a, 0x3e, 0x05, 0xdf, 0x85, 0xcf, - 0x80, 0xd4, 0x23, 0x67, 0x0e, 0x05, 0xf5, 0x03, 0x70, 0x85, 0x23, 0xf2, 0xcc, 0x38, 0x75, 0x9b, - 0xa6, 0x94, 0xf6, 0x94, 0x79, 0x93, 0xdf, 0xfb, 0xbd, 0x37, 0xbf, 0xf7, 0xc7, 0xd0, 0x99, 0xb1, - 0x4c, 0x72, 0x96, 0x04, 0x4b, 0xce, 0x24, 0xc3, 0xfb, 0x29, 0x9b, 0xae, 0x83, 0x69, 0x1e, 0x27, - 0xf3, 0x45, 0x2c, 0x83, 0xd5, 0x2b, 0xef, 0x65, 0x14, 0xcb, 0xb3, 0x7c, 0x1a, 0xcc, 0x58, 0x3a, - 0x8c, 0x58, 0xc4, 0x86, 0x0a, 0x38, 0xcd, 0x4f, 0x95, 0xa5, 0x0c, 0x75, 0xd2, 0x04, 0x5e, 0x2f, - 0x62, 0x2c, 0x4a, 0xe8, 0x35, 0x4a, 0xc6, 0x29, 0x15, 0x32, 0x4c, 0x97, 0x1a, 0xe0, 0x63, 0xd8, - 0x9f, 0xc4, 0x62, 0x71, 0x22, 0xc2, 0x88, 0x12, 0xfa, 0x63, 0x4e, 0x85, 0xf4, 0x8f, 0xe0, 0xdd, - 0xca, 0x9d, 0x58, 0xb2, 0x4c, 0x50, 0xfc, 0x19, 0x38, 0x9c, 0xce, 0x18, 0x9f, 0xbb, 0xa8, 0x6f, - 0x0f, 0x5a, 0xa3, 0x0f, 0x82, 0xdb, 0xb9, 0x05, 0xc6, 0xa1, 0x00, 0x11, 0x03, 0xf6, 0x43, 0x68, - 0x55, 0xae, 0xf1, 0x33, 0xb0, 0x0e, 0x27, 0x2e, 0xea, 0xa3, 0x41, 0x93, 0x58, 0x87, 0x13, 0xec, - 0x42, 0xfd, 0x4d, 0x2e, 0xc3, 0x69, 0x42, 0x5d, 0xab, 0x8f, 0x06, 0x0d, 0x52, 0x9a, 0xf8, 0x3d, - 0xd8, 0x3b, 0xcc, 0x4e, 0x04, 0x75, 0x6d, 0x75, 0xaf, 0x0d, 0x8c, 0xa1, 0x76, 0x1c, 0xff, 0x44, - 0xdd, 0x5a, 0x1f, 0x0d, 0x6c, 0xa2, 0xce, 0xfe, 0xdf, 0x08, 0xda, 0xc7, 0x2c, 0x59, 0x95, 0xf9, - 0xe3, 0x7d, 0xb0, 0x09, 0x3d, 0x35, 0x51, 0x8a, 0x23, 0xee, 0x02, 0x4c, 0xe8, 0x69, 0x9c, 0xc5, - 0x32, 0x66, 0x99, 0x6b, 0xf5, 0xed, 0x41, 0x9b, 0x54, 0x6e, 0xb0, 0x07, 0x8d, 0xd7, 0xe7, 0x4b, - 0xc6, 0x25, 0xe5, 0x2a, 0x5e, 0x93, 0x6c, 0x6c, 0xfc, 0x03, 0x74, 0xca, 0xf3, 0xd7, 0x52, 0x72, - 0xe1, 0xd6, 0xd4, 0xfb, 0x5f, 0x6d, 0xbf, 0xbf, 0x9a, 0x44, 0x70, 0xc3, 0xe7, 0x75, 0x26, 0xf9, - 0x9a, 0xdc, 0xe4, 0xf1, 0xbe, 0x02, 0xbc, 0x0d, 0x2a, 0x92, 0x5f, 0xd0, 0x75, 0x99, 0xfc, 0x82, - 0xae, 0x0b, 0x25, 0x56, 0x61, 0x92, 0x6b, 0x85, 0x9a, 0x44, 0x1b, 0x07, 0xd6, 0xe7, 0xc8, 0xff, - 0x02, 0x3a, 0x26, 0xa6, 0x29, 0xd2, 0x0b, 0xb0, 0x57, 0xf2, 0xdc, 0x54, 0xc8, 0xdd, 0xce, 0xf0, - 0x7b, 0xca, 0x25, 0x3d, 0x27, 0x05, 0xc8, 0xff, 0x10, 0x3a, 0xc7, 0x32, 0x94, 0xb9, 0xd8, 0x29, - 0x9b, 0xff, 0x0b, 0x82, 0x67, 0x25, 0xc6, 0x44, 0xf8, 0x14, 0x1a, 0x2b, 0x45, 0x42, 0xc5, 0x7f, - 0x86, 0xd9, 0x20, 0xf1, 0x01, 0x34, 0x84, 0xe2, 0xa1, 0x42, 0xa9, 0xdf, 0x1a, 0x75, 0x77, 0x79, - 0x99, 0x78, 0x1b, 0x3c, 0x1e, 0x42, 0x2d, 0x61, 0x91, 0x70, 0x6d, 0xe5, 0xf7, 0xfe, 0x2e, 0xbf, - 0xef, 0x58, 0x44, 0x14, 0xd0, 0xbf, 0xb4, 0xc0, 0xd1, 0x77, 0xf8, 0x08, 0x9c, 0x79, 0x1c, 0x51, - 0x21, 0xf5, 0xab, 0xc6, 0xa3, 0x8b, 0xcb, 0xde, 0x3b, 0xbf, 0x5f, 0xf6, 0x5e, 0x54, 0xa6, 0x88, - 0x2d, 0x69, 0x56, 0x4c, 0x5d, 0x18, 0x67, 0x94, 0x8b, 0x61, 0xc4, 0x5e, 0x6a, 0x97, 0x60, 0xa2, - 0x7e, 0x88, 0x61, 0x28, 0xb8, 0xe2, 0x6c, 0x99, 0x4b, 0xfd, 0x82, 0x47, 0x72, 0x69, 0x86, 0xa2, - 0x8d, 0xb3, 0x30, 0xa5, 0xa6, 0xd7, 0xd4, 0x19, 0x3f, 0x07, 0x67, 0x16, 0xce, 0xce, 0xe8, 0x5c, - 0x35, 0x77, 0x83, 0x18, 0x0b, 0x1f, 0x40, 0x5d, 0xc8, 0x90, 0x4b, 0x3a, 0x77, 0xf7, 0xfa, 0x68, - 0xd0, 0x1a, 0x79, 0x81, 0x1e, 0xea, 0xa0, 0x1c, 0xea, 0xe0, 0x6d, 0x39, 0xd4, 0xe3, 0xda, 0xcf, - 0x7f, 0xf4, 0x10, 0x29, 0x1d, 0xf0, 0x97, 0xd0, 0x9c, 0xb1, 0x74, 0x99, 0xd0, 0xc2, 0xdb, 0x79, - 0xa0, 0xf7, 0xb5, 0x4b, 0xd1, 0x7a, 0x94, 0x73, 0xc6, 0xdd, 0xba, 0x6e, 0x3d, 0x65, 0xf8, 0x7f, - 0x59, 0xd0, 0xae, 0x16, 0x6b, 0x6b, 0xaa, 0x8f, 0xc0, 0xd1, 0xa5, 0xd7, 0x2d, 0xfb, 0x38, 0xa9, - 0x34, 0xc3, 0x9d, 0x52, 0xb9, 0x50, 0x9f, 0xe5, 0x9c, 0xd3, 0x4c, 0x9a, 0x45, 0x50, 0x9a, 0x45, - 0xc2, 0x92, 0xc9, 0x30, 0x51, 0x52, 0xd9, 0x44, 0x1b, 0x78, 0x0c, 0xcd, 0xcd, 0xde, 0x7b, 0x80, - 0x0c, 0x8d, 0x22, 0x5d, 0x2d, 0xc5, 0xc6, 0xad, 0x5a, 0x86, 0xfa, 0x93, 0xca, 0xd0, 0xf8, 0xdf, - 0x65, 0xf0, 0x7f, 0x45, 0xd0, 0xdc, 0x74, 0x79, 0x45, 0x5d, 0xf4, 0x64, 0x75, 0x6f, 0x28, 0x63, - 0x3d, 0x4e, 0x99, 0xe7, 0xe0, 0x08, 0xc9, 0x69, 0x98, 0xaa, 0x1a, 0xd9, 0xc4, 0x58, 0xc5, 0x3e, - 0x49, 0x45, 0xa4, 0x2a, 0xd4, 0x26, 0xc5, 0x71, 0xf4, 0x0f, 0x82, 0xfa, 0x37, 0xfa, 0x03, 0x87, - 0xdf, 0x42, 0x73, 0xf3, 0x91, 0xc1, 0xfe, 0xf6, 0x54, 0xdf, 0xfe, 0x2a, 0x79, 0x1f, 0xdd, 0x8b, - 0x31, 0xeb, 0xe9, 0x5b, 0xd8, 0x53, 0x1b, 0x11, 0x77, 0xef, 0x5f, 0xcf, 0x5e, 0x6f, 0xe7, 0xff, - 0x86, 0xe9, 0x0d, 0x38, 0xa6, 0xbb, 0xef, 0x82, 0x56, 0x17, 0xa7, 0xd7, 0xdf, 0x0d, 0xd0, 0x64, - 0x1f, 0xa3, 0x71, 0xfb, 0xe2, 0xaa, 0x8b, 0x7e, 0xbb, 0xea, 0xa2, 0x3f, 0xaf, 0xba, 0x68, 0xea, - 0x28, 0x6d, 0x3f, 0xf9, 0x37, 0x00, 0x00, 0xff, 0xff, 0x97, 0xc3, 0x70, 0xc2, 0xef, 0x07, 0x00, - 0x00, + // 832 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xa4, 0x55, 0x5f, 0x8f, 0xdb, 0x44, + 0x10, 0xc7, 0x76, 0xce, 0x49, 0xe6, 0x72, 0xd5, 0xb1, 0x42, 0x95, 0x65, 0x44, 0x12, 0xcc, 0x4b, + 0x54, 0xa9, 0xbe, 0x36, 0x80, 0x84, 0x0e, 0x09, 0x41, 0x48, 0x25, 0xee, 0xc4, 0xbd, 0xec, 0xb5, + 0xf0, 0xec, 0x24, 0x73, 0xae, 0x15, 0xc7, 0x1b, 0x76, 0xd7, 0xd1, 0x85, 0x4f, 0x01, 0x9f, 0x85, + 0xcf, 0x80, 0xe8, 0x23, 0xcf, 0x3c, 0x1c, 0xe8, 0x3e, 0x00, 0x9f, 0x01, 0xed, 0x1f, 0xa7, 0xbe, + 0xe6, 0xd2, 0x96, 0xeb, 0x93, 0x67, 0xd6, 0xbf, 0x99, 0x9d, 0xf9, 0xcd, 0x9f, 0x85, 0x83, 0x29, + 0x2b, 0x24, 0x67, 0x79, 0xbc, 0xe4, 0x4c, 0x32, 0x72, 0xb8, 0x60, 0x93, 0x75, 0x3c, 0x29, 0xb3, + 0x7c, 0x36, 0xcf, 0x64, 0xbc, 0x7a, 0x1c, 0x3e, 0x4c, 0x33, 0xf9, 0xbc, 0x9c, 0xc4, 0x53, 0xb6, + 0x38, 0x4a, 0x59, 0xca, 0x8e, 0x34, 0x70, 0x52, 0x5e, 0x68, 0x4d, 0x2b, 0x5a, 0x32, 0x0e, 0xc2, + 0x5e, 0xca, 0x58, 0x9a, 0xe3, 0x4b, 0x94, 0xcc, 0x16, 0x28, 0x64, 0xb2, 0x58, 0x1a, 0x40, 0x44, + 0xe0, 0x70, 0x9c, 0x89, 0xf9, 0x33, 0x91, 0xa4, 0x48, 0xf1, 0xa7, 0x12, 0x85, 0x8c, 0x4e, 0xe1, + 0xfd, 0xda, 0x99, 0x58, 0xb2, 0x42, 0x20, 0xf9, 0x1c, 0x7c, 0x8e, 0x53, 0xc6, 0x67, 0x81, 0xd3, + 0xf7, 0x06, 0xfb, 0xc3, 0x8f, 0xe2, 0x57, 0x63, 0x8b, 0xad, 0x81, 0x02, 0x51, 0x0b, 0x8e, 0x12, + 0xd8, 0xaf, 0x1d, 0x93, 0x7b, 0xe0, 0x9e, 0x8c, 0x03, 0xa7, 0xef, 0x0c, 0xda, 0xd4, 0x3d, 0x19, + 0x93, 0x00, 0x9a, 0x67, 0xa5, 0x4c, 0x26, 0x39, 0x06, 0x6e, 0xdf, 0x19, 0xb4, 0x68, 0xa5, 0x92, + 0x0f, 0x60, 0xef, 0xa4, 0x78, 0x26, 0x30, 0xf0, 0xf4, 0xb9, 0x51, 0x08, 0x81, 0xc6, 0x79, 0xf6, + 0x33, 0x06, 0x8d, 0xbe, 0x33, 0xf0, 0xa8, 0x96, 0xa3, 0x5f, 0x5d, 0xe8, 0x9c, 0xb3, 0x7c, 0x55, + 0xc5, 0x4f, 0x0e, 0xc1, 0xa3, 0x78, 0x61, 0x6f, 0x51, 0x22, 0xe9, 0x02, 0x8c, 0xf1, 0x22, 0x2b, + 0x32, 0x99, 0xb1, 0x22, 0x70, 0xfb, 0xde, 0xa0, 0x43, 0x6b, 0x27, 0x24, 0x84, 0xd6, 0x93, 0xcb, + 0x25, 0xe3, 0x12, 0xb9, 0xbe, 0xaf, 0x4d, 0x37, 0x3a, 0xf9, 0x11, 0x0e, 0x2a, 0xf9, 0x1b, 0x29, + 0xb9, 0x08, 0x1a, 0x3a, 0xff, 0xc7, 0xdb, 0xf9, 0xd7, 0x83, 0x88, 0x6f, 0xd8, 0x3c, 0x29, 0x24, + 0x5f, 0xd3, 0x9b, 0x7e, 0x54, 0xee, 0xe7, 0x28, 0x84, 0x8a, 0x68, 0x4f, 0xdf, 0x59, 0xa9, 0xe1, + 0xd7, 0x40, 0xb6, 0xcd, 0x55, 0x5a, 0x73, 0x5c, 0x57, 0x69, 0xcd, 0x71, 0xad, 0x38, 0x5a, 0x25, + 0x79, 0x69, 0xb8, 0x6b, 0x53, 0xa3, 0x1c, 0xbb, 0x5f, 0x38, 0xd1, 0x97, 0x70, 0x60, 0xa3, 0xb1, + 0xe5, 0x7b, 0x00, 0xde, 0x4a, 0x5e, 0xda, 0xda, 0x05, 0xdb, 0xb1, 0xff, 0x80, 0x5c, 0xe2, 0x25, + 0x55, 0xa0, 0xe8, 0x63, 0x38, 0x38, 0x97, 0x89, 0x2c, 0xc5, 0x4e, 0x42, 0xa3, 0xdf, 0x1c, 0xb8, + 0x57, 0x61, 0xec, 0x0d, 0x9f, 0x41, 0x6b, 0xa5, 0x9d, 0xa0, 0x78, 0xe3, 0x35, 0x1b, 0x24, 0x39, + 0x86, 0x96, 0xd0, 0x7e, 0x50, 0xe8, 0xba, 0xec, 0x0f, 0xbb, 0xbb, 0xac, 0xec, 0x7d, 0x1b, 0x3c, + 0x39, 0x82, 0x46, 0xce, 0x52, 0x11, 0x78, 0xda, 0xee, 0xc3, 0x5d, 0x76, 0xdf, 0xb3, 0x94, 0x6a, + 0x60, 0x74, 0xe5, 0x82, 0x6f, 0xce, 0xc8, 0x29, 0xf8, 0xb3, 0x2c, 0x45, 0x21, 0x4d, 0x56, 0xa3, + 0xe1, 0x8b, 0xab, 0xde, 0x7b, 0x7f, 0x5d, 0xf5, 0x1e, 0xd4, 0xe6, 0x8b, 0x2d, 0xb1, 0x50, 0xf3, + 0x98, 0x64, 0x05, 0x72, 0x71, 0x94, 0xb2, 0x87, 0xc6, 0x24, 0x1e, 0xeb, 0x0f, 0xb5, 0x1e, 0x94, + 0xaf, 0xac, 0x58, 0x96, 0xd2, 0x64, 0x70, 0x47, 0x5f, 0xc6, 0x83, 0x6a, 0xf0, 0x22, 0x59, 0xa0, + 0xed, 0x42, 0x2d, 0x93, 0xfb, 0xe0, 0x4f, 0x93, 0xe9, 0x73, 0x9c, 0xe9, 0xb6, 0x6f, 0x51, 0xab, + 0x91, 0x63, 0x68, 0x0a, 0x99, 0x70, 0x89, 0x33, 0xdd, 0x40, 0xfb, 0xc3, 0x30, 0x36, 0xe3, 0x1e, + 0x57, 0xe3, 0x1e, 0x3f, 0xad, 0xc6, 0x7d, 0xd4, 0xf8, 0xe5, 0xef, 0x9e, 0x43, 0x2b, 0x03, 0xf2, + 0x15, 0xb4, 0xa7, 0x6c, 0xb1, 0xcc, 0x51, 0x59, 0xfb, 0x6f, 0x69, 0xfd, 0xd2, 0x44, 0xb5, 0x1e, + 0x72, 0xce, 0x78, 0xd0, 0x34, 0xad, 0xa7, 0x95, 0xe8, 0x5f, 0x17, 0x3a, 0xf5, 0x62, 0x6d, 0xcd, + 0xfb, 0x29, 0xf8, 0xa6, 0xf4, 0xa6, 0x65, 0xef, 0x46, 0x95, 0xf1, 0x70, 0x2b, 0x55, 0x01, 0x34, + 0xa7, 0x25, 0xe7, 0x58, 0x48, 0xbb, 0x22, 0x2a, 0x55, 0x05, 0x2c, 0x99, 0x4c, 0x72, 0x4d, 0x95, + 0x47, 0x8d, 0x42, 0x46, 0xd0, 0xde, 0x6c, 0xc4, 0xb7, 0xa0, 0xa1, 0xa5, 0xc2, 0x35, 0x54, 0x6c, + 0xcc, 0xea, 0x65, 0x68, 0xbe, 0x53, 0x19, 0x5a, 0xff, 0xbb, 0x0c, 0xd1, 0xef, 0x0e, 0xb4, 0x37, + 0x5d, 0x5e, 0x63, 0xd7, 0x79, 0x67, 0x76, 0x6f, 0x30, 0xe3, 0xde, 0x8d, 0x99, 0xfb, 0xe0, 0x0b, + 0xc9, 0x31, 0x59, 0xe8, 0x1a, 0x79, 0xd4, 0x6a, 0x6a, 0x9f, 0x2c, 0x44, 0xaa, 0x2b, 0xd4, 0xa1, + 0x4a, 0x8c, 0x22, 0xe8, 0x8c, 0xd6, 0x12, 0xc5, 0x19, 0x0a, 0xf5, 0x5a, 0xa8, 0xda, 0xce, 0x12, + 0x99, 0xe8, 0x3c, 0x3a, 0x54, 0xcb, 0xc3, 0x3f, 0x5c, 0x68, 0x7e, 0x6b, 0x9e, 0x47, 0xf2, 0x14, + 0xda, 0x9b, 0x27, 0x8a, 0x44, 0xdb, 0x93, 0xff, 0xea, 0x9b, 0x16, 0x7e, 0xf2, 0x5a, 0x8c, 0x5d, + 0x61, 0xdf, 0xc1, 0x9e, 0xde, 0x9a, 0xa4, 0xfb, 0xfa, 0xe5, 0x1e, 0xf6, 0x76, 0xfe, 0xb7, 0x9e, + 0xce, 0xc0, 0xb7, 0x13, 0x70, 0x1b, 0xb4, 0xbe, 0x5c, 0xc3, 0xfe, 0x6e, 0x80, 0x71, 0xf6, 0xc8, + 0x21, 0x67, 0x9b, 0xa7, 0xe2, 0xb6, 0xd0, 0xea, 0xcc, 0x85, 0x6f, 0xf8, 0x3f, 0x70, 0x1e, 0x39, + 0xa3, 0xce, 0x8b, 0xeb, 0xae, 0xf3, 0xe7, 0x75, 0xd7, 0xf9, 0xe7, 0xba, 0xeb, 0x4c, 0x7c, 0x5d, + 0xce, 0x4f, 0xff, 0x0b, 0x00, 0x00, 0xff, 0xff, 0xc9, 0xc1, 0x1a, 0x81, 0x7c, 0x08, 0x00, 0x00, } diff --git a/api/services/control/control.proto b/api/services/control/control.proto index d4ad20748..4a4af389b 100644 --- a/api/services/control/control.proto +++ b/api/services/control/control.proto @@ -13,6 +13,7 @@ service Control { rpc DiskUsage(DiskUsageRequest) returns (DiskUsageResponse); rpc Solve(SolveRequest) returns (SolveResponse); rpc Status(StatusRequest) returns (stream StatusResponse); + rpc Session(stream BytesMessage) returns (stream BytesMessage); } message DiskUsageRequest { @@ -34,6 +35,7 @@ message SolveRequest { repeated bytes Definition = 2; // TODO: remove repeated string Exporter = 3; map ExporterAttrs = 4; + string Session = 5; } message SolveResponse { @@ -78,3 +80,7 @@ message VertexLog { int64 stream = 3; bytes msg = 4; } + +message BytesMessage { + bytes data = 1; +} \ No newline at end of file diff --git a/client/client_test.go b/client/client_test.go index dadf6f8f1..3b07ef679 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -106,6 +106,6 @@ func testBuildMultiMount(t *testing.T, address string) { err = llb.WriteTo(dt, buf) assert.Nil(t, err) - err = c.Solve(context.TODO(), buf, nil, "", nil) + err = c.Solve(context.TODO(), buf, nil, "", nil, "") assert.Nil(t, err) } diff --git a/client/llb/llb.go b/client/llb/llb.go index 65605baaf..aa1736b00 100644 --- a/client/llb/llb.go +++ b/client/llb/llb.go @@ -78,6 +78,18 @@ func Scratch() *State { return s } +func Local(name string) *State { + return Source("local://" + name) +} + +type LocalOption func(*source) + +func SessionID(id string) LocalOption { + return func(s *source) { + s.attrs[pb.AttrLocalSessionID] = id + } +} + type exec struct { meta Meta mounts []*mount diff --git a/client/local.go b/client/local.go new file mode 100644 index 000000000..25c977079 --- /dev/null +++ b/client/local.go @@ -0,0 +1,5 @@ +package client + +func getSharedKey(dir string) (string, error) { + return dir, nil // not implemented +} diff --git a/client/solve.go b/client/solve.go index a9cbf144d..4b71d5187 100644 --- a/client/solve.go +++ b/client/solve.go @@ -5,15 +5,29 @@ import ( "crypto/rand" "encoding/hex" "io" + "os" + "path/filepath" + "strings" "time" + "github.com/Sirupsen/logrus" controlapi "github.com/moby/buildkit/api/services/control" "github.com/moby/buildkit/client/llb" + "github.com/moby/buildkit/session" + "github.com/moby/buildkit/session/filesync" + "github.com/moby/buildkit/session/grpchijack" + "github.com/moby/buildkit/solver/pb" "github.com/pkg/errors" "golang.org/x/sync/errgroup" ) -func (c *Client) Solve(ctx context.Context, r io.Reader, statusChan chan *SolveStatus, exporter string, exporterAttrs map[string]string) error { +func (c *Client) Solve(ctx context.Context, r io.Reader, statusChan chan *SolveStatus, exporter string, exporterAttrs map[string]string, localDir string) error { + defer func() { + if statusChan != nil { + close(statusChan) + } + }() + def, err := llb.ReadFrom(r) if err != nil { return errors.Wrap(err, "failed to parse input") @@ -23,10 +37,34 @@ func (c *Client) Solve(ctx context.Context, r io.Reader, statusChan chan *SolveS return errors.New("invalid empty definition") } + if err := validateLocals(def, localDir); err != nil { + return err + } + ref := generateID() eg, ctx := errgroup.WithContext(ctx) statusContext, cancelStatus := context.WithCancel(context.Background()) + defer cancelStatus() + + sharedKey, err := getSharedKey(localDir) + if err != nil { + return errors.Wrap(err, "failed to get build shared key") + } + s, err := session.NewSession(filepath.Base(localDir), sharedKey) + if err != nil { + return errors.Wrap(err, "failed to create session") + } + + if localDir != "" { + _, dir, _ := parseLocalDir(localDir) + workdirProvider := filesync.NewFSSyncProvider(dir, nil) + s.Allow(workdirProvider) + } + + eg.Go(func() error { + return s.Run(ctx, grpchijack.Dialer(c.controlClient())) + }) eg.Go(func() error { defer func() { // make sure the Status ends cleanly on build errors @@ -34,12 +72,15 @@ func (c *Client) Solve(ctx context.Context, r io.Reader, statusChan chan *SolveS <-time.After(3 * time.Second) cancelStatus() }() + logrus.Debugf("stopping session") + s.Close() }() _, err = c.controlClient().Solve(ctx, &controlapi.SolveRequest{ Ref: ref, Definition: def, Exporter: exporter, ExporterAttrs: exporterAttrs, + Session: s.UUID(), }) if err != nil { return errors.Wrap(err, "failed to solve") @@ -100,12 +141,6 @@ func (c *Client) Solve(ctx context.Context, r io.Reader, statusChan chan *SolveS } }) - defer func() { - if statusChan != nil { - close(statusChan) - } - }() - return eg.Wait() } @@ -116,3 +151,44 @@ func generateID() string { } return hex.EncodeToString(b) } + +func validateLocals(defs [][]byte, localDir string) error { + k, _, err := parseLocalDir(localDir) + if err != nil { + return err + } + for _, dt := range defs { + var op pb.Op + if err := (&op).Unmarshal(dt); err != nil { + return errors.Wrap(err, "failed to parse llb proto op") + } + if src := op.GetSource(); src != nil { + if strings.HasPrefix(src.Identifier, "local://") { // TODO: just make a type property + name := strings.TrimPrefix(src.Identifier, "local://") + if name != k { + return errors.Errorf("local directory %s not enabled", name) + } + } + } + } + return nil +} + +func parseLocalDir(str string) (string, string, error) { + if str == "" { + return "", "", nil + } + parts := strings.SplitN(str, "=", 2) + if len(parts) != 2 { + return "", "", errors.Errorf("invalid local indentifier %q, need name=dir", str) + } + fi, err := os.Stat(parts[1]) + if err != nil { + return "", "", errors.Wrapf(err, "could not find %s", parts[1]) + } + if !fi.IsDir() { + return "", "", errors.Errorf("%s not a directory", parts[1]) + } + return parts[0], parts[1], nil + +} diff --git a/cmd/buildctl/build.go b/cmd/buildctl/build.go index 06b2adf80..6e597ea32 100644 --- a/cmd/buildctl/build.go +++ b/cmd/buildctl/build.go @@ -33,6 +33,10 @@ var buildCommand = cli.Command{ Name: "no-progress", Usage: "Don't show interactive progress", }, + cli.StringFlag{ + Name: "local", + Usage: "Allow build access to the local directory", + }, }, } @@ -61,7 +65,7 @@ func build(clicontext *cli.Context) error { } eg.Go(func() error { - return c.Solve(ctx, os.Stdin, ch, clicontext.String("exporter"), exporterAttrs) + return c.Solve(ctx, os.Stdin, ch, clicontext.String("exporter"), exporterAttrs, clicontext.String("local")) }) eg.Go(func() error { diff --git a/control/control.go b/control/control.go index 51e9618b1..95e214db9 100644 --- a/control/control.go +++ b/control/control.go @@ -1,11 +1,14 @@ package control import ( + "github.com/Sirupsen/logrus" "github.com/containerd/containerd/snapshot" controlapi "github.com/moby/buildkit/api/services/control" "github.com/moby/buildkit/cache" "github.com/moby/buildkit/client" "github.com/moby/buildkit/exporter" + "github.com/moby/buildkit/session" + "github.com/moby/buildkit/session/grpchijack" "github.com/moby/buildkit/solver" "github.com/moby/buildkit/source" "github.com/moby/buildkit/worker" @@ -22,6 +25,7 @@ type Opt struct { SourceManager *source.Manager InstructionCache solver.InstructionCache Exporters map[string]exporter.Exporter + SessionManager *session.Manager } type Controller struct { // TODO: ControlService @@ -83,6 +87,8 @@ func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (* } } + ctx = session.NewContext(ctx, req.Session) + if err := c.solver.Solve(ctx, req.Ref, v, expi); err != nil { return nil, err } @@ -147,3 +153,12 @@ func (c *Controller) Status(req *controlapi.StatusRequest, stream controlapi.Con return eg.Wait() } + +func (c *Controller) Session(stream controlapi.Control_SessionServer) error { + logrus.Debugf("session started") + conn, opts := grpchijack.Hijack(stream) + defer conn.Close() + err := c.opt.SessionManager.HandleConn(stream.Context(), conn, opts) + logrus.Debugf("session finished: %v", err) + return err +} diff --git a/control/control_default.go b/control/control_default.go index a313fe0c3..ab21bfaff 100644 --- a/control/control_default.go +++ b/control/control_default.go @@ -14,10 +14,12 @@ import ( "github.com/moby/buildkit/cache/metadata" "github.com/moby/buildkit/exporter" imageexporter "github.com/moby/buildkit/exporter/containerimage" + "github.com/moby/buildkit/session" "github.com/moby/buildkit/snapshot/blobmapping" "github.com/moby/buildkit/source" "github.com/moby/buildkit/source/containerimage" "github.com/moby/buildkit/source/git" + "github.com/moby/buildkit/source/local" ) const keyImageExporter = "image" @@ -85,6 +87,20 @@ func defaultControllerOpts(root string, pd pullDeps) (*Opt, error) { sm.Register(gs) + sessm, err := session.NewManager() + if err != nil { + return nil, err + } + + ss, err := local.NewSource(local.Opt{ + SessionManager: sessm, + CacheAccessor: cm, + }) + if err != nil { + return nil, err + } + sm.Register(ss) + exporters := map[string]exporter.Exporter{} imageExporter, err := imageexporter.New(imageexporter.Opt{ @@ -105,5 +121,6 @@ func defaultControllerOpts(root string, pd pullDeps) (*Opt, error) { SourceManager: sm, InstructionCache: ic, Exporters: exporters, + SessionManager: sessm, }, nil } diff --git a/session/context.go b/session/context.go new file mode 100644 index 000000000..4f10f30c1 --- /dev/null +++ b/session/context.go @@ -0,0 +1,22 @@ +package session + +import "context" + +type contextKeyT string + +var contextKey = contextKeyT("buildkit/session-uuid") + +func NewContext(ctx context.Context, uuid string) context.Context { + if uuid != "" { + return context.WithValue(ctx, contextKey, uuid) + } + return ctx +} + +func FromContext(ctx context.Context) string { + v := ctx.Value(contextKey) + if v == nil { + return "" + } + return v.(string) +} diff --git a/session/filesync/diffcopy.go b/session/filesync/diffcopy.go index 533847acd..ab5c549aa 100644 --- a/session/filesync/diffcopy.go +++ b/session/filesync/diffcopy.go @@ -3,10 +3,9 @@ package filesync import ( "time" - "google.golang.org/grpc" - "github.com/Sirupsen/logrus" "github.com/tonistiigi/fsutil" + "google.golang.org/grpc" ) func sendDiffCopy(stream grpc.Stream, dir string, includes, excludes []string, progress progressCb) error { diff --git a/session/filesync/filesync.go b/session/filesync/filesync.go index fe4d00a72..2a1ea365a 100644 --- a/session/filesync/filesync.go +++ b/session/filesync/filesync.go @@ -1,6 +1,7 @@ package filesync import ( + "fmt" "os" "strings" @@ -177,6 +178,8 @@ func FSSync(ctx context.Context, c session.Caller, opt FSSendRequestOpt) error { return err } stream = cc + default: + panic(fmt.Sprintf("invalid protocol: %q", pr.name)) } return pr.recvFn(stream, opt.DestDir, opt.CacheUpdater) diff --git a/session/grpchijack/dial.go b/session/grpchijack/dial.go new file mode 100644 index 000000000..c820bb9a1 --- /dev/null +++ b/session/grpchijack/dial.go @@ -0,0 +1,123 @@ +package grpchijack + +import ( + "net" + "strings" + "sync" + "time" + + controlapi "github.com/moby/buildkit/api/services/control" + "github.com/moby/buildkit/session" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" +) + +var bufPool = sync.Pool{ + New: func() interface{} { + return make([]byte, 32*1<<10) + }, +} + +func Dialer(api controlapi.ControlClient) session.Dialer { + return func(ctx context.Context, proto string, meta map[string][]string) (net.Conn, error) { + + meta = lowerHeaders(meta) + + md := metadata.MD(meta) + + ctx = metadata.NewContext(context.Background(), md) + + stream, err := api.Session(ctx) + if err != nil { + return nil, err + } + + return streamToConn(stream), nil + } +} + +func streamToConn(stream grpc.Stream) net.Conn { + return &conn{stream: stream, buf: make([]byte, 32*1<<10)} +} + +type conn struct { + stream grpc.Stream + buf []byte + lastBuf []byte +} + +func (c *conn) Read(b []byte) (int, error) { + if c.lastBuf != nil { + n := copy(b, c.lastBuf) + c.lastBuf = c.lastBuf[n:] + if len(c.lastBuf) == 0 { + c.lastBuf = nil + } + return n, nil + } + m := new(controlapi.BytesMessage) + m.Data = c.buf + + if err := c.stream.RecvMsg(m); err != nil { + return 0, err + } + c.buf = m.Data[:cap(m.Data)] + + n := copy(b, m.Data) + if n < len(m.Data) { + c.lastBuf = m.Data[n:] + } + + return n, nil +} + +func (c *conn) Write(b []byte) (int, error) { + m := &controlapi.BytesMessage{Data: b} + if err := c.stream.SendMsg(m); err != nil { + return 0, err + } + return len(b), nil +} + +func (s *conn) Close() error { + if cs, ok := s.stream.(grpc.ClientStream); ok { + return cs.CloseSend() + } + return nil +} + +func (s *conn) LocalAddr() net.Addr { + return dummyAddr{} +} +func (s *conn) RemoteAddr() net.Addr { + return dummyAddr{} +} +func (s *conn) SetDeadline(t time.Time) error { + return nil +} +func (s *conn) SetReadDeadline(t time.Time) error { + return nil +} +func (s *conn) SetWriteDeadline(t time.Time) error { + return nil +} + +type dummyAddr struct { +} + +func (d dummyAddr) Network() string { + return "tcp" +} + +func (d dummyAddr) String() string { + return "localhost" +} + +func lowerHeaders(in map[string][]string) map[string][]string { + out := map[string][]string{} + for k := range in { + out[strings.ToLower(k)] = in[k] + } + return out +} diff --git a/session/grpchijack/hijack.go b/session/grpchijack/hijack.go new file mode 100644 index 000000000..2c9cc81bf --- /dev/null +++ b/session/grpchijack/hijack.go @@ -0,0 +1,13 @@ +package grpchijack + +import ( + "net" + + controlapi "github.com/moby/buildkit/api/services/control" + "google.golang.org/grpc/metadata" +) + +func Hijack(stream controlapi.Control_SessionServer) (net.Conn, map[string][]string) { + md, _ := metadata.FromContext(stream.Context()) + return streamToConn(stream), md +} diff --git a/session/manager.go b/session/manager.go index 9523e6f31..e44957fed 100644 --- a/session/manager.go +++ b/session/manager.go @@ -102,6 +102,8 @@ func (sm *Manager) handleConn(ctx context.Context, conn net.Conn, opts map[strin ctx, cancel := context.WithCancel(ctx) defer cancel() + opts = canonicalHeaders(opts) + h := http.Header(opts) uuid := h.Get(headerSessionUUID) name := h.Get(headerSessionName) @@ -200,3 +202,11 @@ func (c *client) Supports(url string) bool { func (c *client) Conn() *grpc.ClientConn { return c.cc } + +func canonicalHeaders(in map[string][]string) map[string][]string { + out := map[string][]string{} + for k := range in { + out[http.CanonicalHeaderKey(k)] = in[k] + } + return out +} diff --git a/session/session.go b/session/session.go index 147486a75..d29f6f47a 100644 --- a/session/session.go +++ b/session/session.go @@ -92,6 +92,7 @@ func (s *Session) Run(ctx context.Context, dialer Dialer) error { // Close closes the session func (s *Session) Close() error { if s.cancelCtx != nil && s.done != nil { + s.grpcServer.Stop() s.cancelCtx() <-s.done } diff --git a/solver/pb/attr.go b/solver/pb/attr.go index 64ac3886d..0e84fe6a4 100644 --- a/solver/pb/attr.go +++ b/solver/pb/attr.go @@ -1,3 +1,4 @@ package pb const AttrKeepGitDir = "git.keepgitdir" +const AttrLocalSessionID = "local.session" diff --git a/solver/source.go b/solver/source.go index 378aa25fa..e551bf707 100644 --- a/solver/source.go +++ b/solver/source.go @@ -42,6 +42,14 @@ func (s *sourceOp) instance(ctx context.Context) (source.SourceInstance, error) } } } + if id, ok := id.(*source.LocalIdentifier); ok { + for k, v := range s.op.Source.Attrs { + switch k { + case pb.AttrLocalSessionID: + id.SessionID = v + } + } + } src, err := s.sm.Resolve(ctx, id) if err != nil { return nil, err diff --git a/solver/vertex.go b/solver/vertex.go index b30b24368..a6cb99280 100644 --- a/solver/vertex.go +++ b/solver/vertex.go @@ -83,6 +83,7 @@ func (v *vertex) notifyStarted(ctx context.Context) { defer pw.Close() now := time.Now() v.clientVertex.Started = &now + v.clientVertex.Completed = nil pw.Write(v.Digest().String(), v.clientVertex) } diff --git a/source/identifier.go b/source/identifier.go index 926ffb5a9..0bd364188 100644 --- a/source/identifier.go +++ b/source/identifier.go @@ -15,6 +15,7 @@ var ( const ( DockerImageScheme = "docker-image" GitScheme = "git" + LocalScheme = "local" ) type Identifier interface { @@ -33,6 +34,8 @@ func FromString(s string) (Identifier, error) { return NewImageIdentifier(parts[1]) case GitScheme: return NewGitIdentifier(parts[1]) + case LocalScheme: + return NewLocalIdentifier(parts[1]) default: return nil, errors.Wrapf(errNotFound, "unknown schema %s", parts[0]) } @@ -54,6 +57,19 @@ func NewImageIdentifier(str string) (*ImageIdentifier, error) { return &ImageIdentifier{Reference: ref}, nil } -func (i *ImageIdentifier) ID() string { +func (_ *ImageIdentifier) ID() string { return DockerImageScheme } + +type LocalIdentifier struct { + Name string + SessionID string +} + +func NewLocalIdentifier(str string) (*LocalIdentifier, error) { + return &LocalIdentifier{Name: str}, nil +} + +func (_ *LocalIdentifier) ID() string { + return LocalScheme +} diff --git a/source/local/local.go b/source/local/local.go new file mode 100644 index 000000000..f95d97713 --- /dev/null +++ b/source/local/local.go @@ -0,0 +1,138 @@ +package local + +import ( + "time" + + "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/session" + "github.com/moby/buildkit/session/filesync" + "github.com/moby/buildkit/snapshot" + "github.com/moby/buildkit/source" + "github.com/pkg/errors" + "golang.org/x/net/context" +) + +type Opt struct { + SessionManager *session.Manager + CacheAccessor cache.Accessor +} + +func NewSource(opt Opt) (source.Source, error) { + ls := &localSource{ + sm: opt.SessionManager, + cm: opt.CacheAccessor, + } + return ls, nil +} + +type localSource struct { + sm *session.Manager + cm cache.Accessor +} + +func (ls *localSource) ID() string { + return source.LocalScheme +} + +func (ls *localSource) Resolve(ctx context.Context, id source.Identifier) (source.SourceInstance, error) { + localIdentifier, ok := id.(*source.LocalIdentifier) + if !ok { + return nil, errors.Errorf("invalid local identifier %v", id) + } + + return &localSourceHandler{ + src: *localIdentifier, + localSource: ls, + }, nil +} + +type localSourceHandler struct { + src source.LocalIdentifier + *localSource +} + +func (ls *localSourceHandler) CacheKey(ctx context.Context) (string, error) { + sessionID := ls.src.SessionID + + if sessionID == "" { + uuid := session.FromContext(ctx) + if uuid == "" { + return "", errors.New("could not access local files without session") + } + sessionID = uuid + } + + return "session:" + ls.src.Name + ":" + sessionID, nil +} + +func (ls *localSourceHandler) Snapshot(ctx context.Context) (out cache.ImmutableRef, retErr error) { + + uuid := session.FromContext(ctx) + if uuid == "" { + return nil, errors.New("could not access local files without session") + } + + timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + + caller, err := ls.sm.Get(timeoutCtx, uuid) + if err != nil { + return nil, err + } + + mutable, err := ls.cm.New(ctx, nil) + if err != nil { + return nil, err + } + + defer func() { + if retErr != nil && mutable != nil { + s, err := mutable.Freeze() + if err == nil { + go s.Release(context.TODO()) + } + } + }() + + mount, err := mutable.Mount(ctx) + if err != nil { + return nil, err + } + + lm := snapshot.LocalMounter(mount) + + dest, err := lm.Mount() + if err != nil { + return nil, err + } + + defer func() { + if retErr != nil && lm != nil { + lm.Unmount() + } + }() + + opt := filesync.FSSendRequestOpt{ + IncludePatterns: nil, + OverrideExcludes: false, + DestDir: dest, + CacheUpdater: nil, + } + + if err := filesync.FSSync(ctx, caller, opt); err != nil { + return nil, err + } + + if err := lm.Unmount(); err != nil { + return nil, err + } + lm = nil + + snap, err := mutable.ReleaseAndCommit(ctx) + if err != nil { + return nil, err + } + mutable = nil + + return snap, err +}