diff --git a/components/ide/code/leeway.Dockerfile b/components/ide/code/leeway.Dockerfile index 38663de001f713..5bd2a9e7af2a40 100644 --- a/components/ide/code/leeway.Dockerfile +++ b/components/ide/code/leeway.Dockerfile @@ -42,7 +42,7 @@ RUN curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | && npm install -g yarn node-gyp ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH -ENV GP_CODE_COMMIT 9bc86ad1d6f3f7116ef96003983738963fd70a6f +ENV GP_CODE_COMMIT 77c0ff0c18222149c9c5c7f01b8856cdb41071cc RUN mkdir gp-code \ && cd gp-code \ && git init \ diff --git a/components/local-app-api/go/localapp.pb.go b/components/local-app-api/go/localapp.pb.go index 43aaf8681471fb..c615664d2840c6 100644 --- a/components/local-app-api/go/localapp.pb.go +++ b/components/local-app-api/go/localapp.pb.go @@ -5,7 +5,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.15.5 +// protoc v3.17.3 // source: localapp.proto package api @@ -285,6 +285,116 @@ func (*AutoTunnelResponse) Descriptor() ([]byte, []int) { return file_localapp_proto_rawDescGZIP(), []int{4} } +type ResolveSSHConnectionRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + InstanceId string `protobuf:"bytes,1,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"` + WorkspaceId string `protobuf:"bytes,2,opt,name=workspace_id,json=workspaceId,proto3" json:"workspace_id,omitempty"` +} + +func (x *ResolveSSHConnectionRequest) Reset() { + *x = ResolveSSHConnectionRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_localapp_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResolveSSHConnectionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResolveSSHConnectionRequest) ProtoMessage() {} + +func (x *ResolveSSHConnectionRequest) ProtoReflect() protoreflect.Message { + mi := &file_localapp_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResolveSSHConnectionRequest.ProtoReflect.Descriptor instead. +func (*ResolveSSHConnectionRequest) Descriptor() ([]byte, []int) { + return file_localapp_proto_rawDescGZIP(), []int{5} +} + +func (x *ResolveSSHConnectionRequest) GetInstanceId() string { + if x != nil { + return x.InstanceId + } + return "" +} + +func (x *ResolveSSHConnectionRequest) GetWorkspaceId() string { + if x != nil { + return x.WorkspaceId + } + return "" +} + +type ResolveSSHConnectionResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ConfigFile string `protobuf:"bytes,1,opt,name=config_file,json=configFile,proto3" json:"config_file,omitempty"` + Host string `protobuf:"bytes,2,opt,name=host,proto3" json:"host,omitempty"` +} + +func (x *ResolveSSHConnectionResponse) Reset() { + *x = ResolveSSHConnectionResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_localapp_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResolveSSHConnectionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResolveSSHConnectionResponse) ProtoMessage() {} + +func (x *ResolveSSHConnectionResponse) ProtoReflect() protoreflect.Message { + mi := &file_localapp_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResolveSSHConnectionResponse.ProtoReflect.Descriptor instead. +func (*ResolveSSHConnectionResponse) Descriptor() ([]byte, []int) { + return file_localapp_proto_rawDescGZIP(), []int{6} +} + +func (x *ResolveSSHConnectionResponse) GetConfigFile() string { + if x != nil { + return x.ConfigFile + } + return "" +} + +func (x *ResolveSSHConnectionResponse) GetHost() string { + if x != nil { + return x.Host + } + return "" +} + var File_localapp_proto protoreflect.FileDescriptor var file_localapp_proto_rawDesc = []byte{ @@ -316,21 +426,39 @@ var file_localapp_proto_rawDesc = []byte{ 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x14, 0x0a, 0x12, 0x41, 0x75, 0x74, 0x6f, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xa8, 0x01, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x41, - 0x70, 0x70, 0x12, 0x51, 0x0a, 0x0c, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x61, 0x70, 0x70, 0x2e, 0x54, 0x75, - 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x61, 0x70, 0x70, 0x2e, 0x54, 0x75, 0x6e, - 0x6e, 0x65, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x49, 0x0a, 0x0a, 0x41, 0x75, 0x74, 0x6f, 0x54, 0x75, 0x6e, - 0x6e, 0x65, 0x6c, 0x12, 0x1b, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x61, 0x70, 0x70, 0x2e, 0x41, - 0x75, 0x74, 0x6f, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1c, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x61, 0x70, 0x70, 0x2e, 0x41, 0x75, 0x74, 0x6f, - 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, - 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2d, 0x69, 0x6f, 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2f, - 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x2d, 0x61, 0x70, 0x70, 0x2f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x61, 0x0a, 0x1b, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, + 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x77, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x22, 0x53, 0x0a, 0x1c, 0x52, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x32, 0x91, 0x02, + 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x41, 0x70, 0x70, 0x12, 0x51, 0x0a, 0x0c, 0x54, 0x75, + 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6f, 0x63, + 0x61, 0x6c, 0x61, 0x70, 0x70, 0x2e, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6f, 0x63, 0x61, + 0x6c, 0x61, 0x70, 0x70, 0x2e, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x49, 0x0a, + 0x0a, 0x41, 0x75, 0x74, 0x6f, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1b, 0x2e, 0x6c, 0x6f, + 0x63, 0x61, 0x6c, 0x61, 0x70, 0x70, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x54, 0x75, 0x6e, 0x6e, 0x65, + 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, + 0x61, 0x70, 0x70, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x67, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x25, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x61, 0x70, 0x70, 0x2e, 0x52, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x61, + 0x70, 0x70, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2d, 0x69, 0x6f, 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, + 0x2f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x2d, 0x61, 0x70, 0x70, 0x2f, 0x61, 0x70, 0x69, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -345,24 +473,28 @@ func file_localapp_proto_rawDescGZIP() []byte { return file_localapp_proto_rawDescData } -var file_localapp_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_localapp_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_localapp_proto_goTypes = []interface{}{ - (*TunnelStatusRequest)(nil), // 0: localapp.TunnelStatusRequest - (*TunnelStatusResponse)(nil), // 1: localapp.TunnelStatusResponse - (*TunnelStatus)(nil), // 2: localapp.TunnelStatus - (*AutoTunnelRequest)(nil), // 3: localapp.AutoTunnelRequest - (*AutoTunnelResponse)(nil), // 4: localapp.AutoTunnelResponse - (api.TunnelVisiblity)(0), // 5: supervisor.TunnelVisiblity + (*TunnelStatusRequest)(nil), // 0: localapp.TunnelStatusRequest + (*TunnelStatusResponse)(nil), // 1: localapp.TunnelStatusResponse + (*TunnelStatus)(nil), // 2: localapp.TunnelStatus + (*AutoTunnelRequest)(nil), // 3: localapp.AutoTunnelRequest + (*AutoTunnelResponse)(nil), // 4: localapp.AutoTunnelResponse + (*ResolveSSHConnectionRequest)(nil), // 5: localapp.ResolveSSHConnectionRequest + (*ResolveSSHConnectionResponse)(nil), // 6: localapp.ResolveSSHConnectionResponse + (api.TunnelVisiblity)(0), // 7: supervisor.TunnelVisiblity } var file_localapp_proto_depIdxs = []int32{ 2, // 0: localapp.TunnelStatusResponse.tunnels:type_name -> localapp.TunnelStatus - 5, // 1: localapp.TunnelStatus.visibility:type_name -> supervisor.TunnelVisiblity + 7, // 1: localapp.TunnelStatus.visibility:type_name -> supervisor.TunnelVisiblity 0, // 2: localapp.LocalApp.TunnelStatus:input_type -> localapp.TunnelStatusRequest 3, // 3: localapp.LocalApp.AutoTunnel:input_type -> localapp.AutoTunnelRequest - 1, // 4: localapp.LocalApp.TunnelStatus:output_type -> localapp.TunnelStatusResponse - 4, // 5: localapp.LocalApp.AutoTunnel:output_type -> localapp.AutoTunnelResponse - 4, // [4:6] is the sub-list for method output_type - 2, // [2:4] is the sub-list for method input_type + 5, // 4: localapp.LocalApp.ResolveSSHConnection:input_type -> localapp.ResolveSSHConnectionRequest + 1, // 5: localapp.LocalApp.TunnelStatus:output_type -> localapp.TunnelStatusResponse + 4, // 6: localapp.LocalApp.AutoTunnel:output_type -> localapp.AutoTunnelResponse + 6, // 7: localapp.LocalApp.ResolveSSHConnection:output_type -> localapp.ResolveSSHConnectionResponse + 5, // [5:8] is the sub-list for method output_type + 2, // [2:5] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name @@ -434,6 +566,30 @@ func file_localapp_proto_init() { return nil } } + file_localapp_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResolveSSHConnectionRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_localapp_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResolveSSHConnectionResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -441,7 +597,7 @@ func file_localapp_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_localapp_proto_rawDesc, NumEnums: 0, - NumMessages: 5, + NumMessages: 7, NumExtensions: 0, NumServices: 1, }, diff --git a/components/local-app-api/go/localapp_grpc.pb.go b/components/local-app-api/go/localapp_grpc.pb.go index 21e5984222b35c..72a4704b3e1efa 100644 --- a/components/local-app-api/go/localapp_grpc.pb.go +++ b/components/local-app-api/go/localapp_grpc.pb.go @@ -24,6 +24,7 @@ const _ = grpc.SupportPackageIsVersion7 type LocalAppClient interface { TunnelStatus(ctx context.Context, in *TunnelStatusRequest, opts ...grpc.CallOption) (LocalApp_TunnelStatusClient, error) AutoTunnel(ctx context.Context, in *AutoTunnelRequest, opts ...grpc.CallOption) (*AutoTunnelResponse, error) + ResolveSSHConnection(ctx context.Context, in *ResolveSSHConnectionRequest, opts ...grpc.CallOption) (*ResolveSSHConnectionResponse, error) } type localAppClient struct { @@ -75,12 +76,22 @@ func (c *localAppClient) AutoTunnel(ctx context.Context, in *AutoTunnelRequest, return out, nil } +func (c *localAppClient) ResolveSSHConnection(ctx context.Context, in *ResolveSSHConnectionRequest, opts ...grpc.CallOption) (*ResolveSSHConnectionResponse, error) { + out := new(ResolveSSHConnectionResponse) + err := c.cc.Invoke(ctx, "/localapp.LocalApp/ResolveSSHConnection", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // LocalAppServer is the server API for LocalApp service. // All implementations must embed UnimplementedLocalAppServer // for forward compatibility type LocalAppServer interface { TunnelStatus(*TunnelStatusRequest, LocalApp_TunnelStatusServer) error AutoTunnel(context.Context, *AutoTunnelRequest) (*AutoTunnelResponse, error) + ResolveSSHConnection(context.Context, *ResolveSSHConnectionRequest) (*ResolveSSHConnectionResponse, error) mustEmbedUnimplementedLocalAppServer() } @@ -94,6 +105,9 @@ func (UnimplementedLocalAppServer) TunnelStatus(*TunnelStatusRequest, LocalApp_T func (UnimplementedLocalAppServer) AutoTunnel(context.Context, *AutoTunnelRequest) (*AutoTunnelResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AutoTunnel not implemented") } +func (UnimplementedLocalAppServer) ResolveSSHConnection(context.Context, *ResolveSSHConnectionRequest) (*ResolveSSHConnectionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ResolveSSHConnection not implemented") +} func (UnimplementedLocalAppServer) mustEmbedUnimplementedLocalAppServer() {} // UnsafeLocalAppServer may be embedded to opt out of forward compatibility for this service. @@ -146,6 +160,24 @@ func _LocalApp_AutoTunnel_Handler(srv interface{}, ctx context.Context, dec func return interceptor(ctx, in, info, handler) } +func _LocalApp_ResolveSSHConnection_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ResolveSSHConnectionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LocalAppServer).ResolveSSHConnection(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/localapp.LocalApp/ResolveSSHConnection", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LocalAppServer).ResolveSSHConnection(ctx, req.(*ResolveSSHConnectionRequest)) + } + return interceptor(ctx, in, info, handler) +} + // LocalApp_ServiceDesc is the grpc.ServiceDesc for LocalApp service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -157,6 +189,10 @@ var LocalApp_ServiceDesc = grpc.ServiceDesc{ MethodName: "AutoTunnel", Handler: _LocalApp_AutoTunnel_Handler, }, + { + MethodName: "ResolveSSHConnection", + Handler: _LocalApp_ResolveSSHConnection_Handler, + }, }, Streams: []grpc.StreamDesc{ { diff --git a/components/local-app-api/localapp.proto b/components/local-app-api/localapp.proto index 90fd706c03af39..51fd22283059bd 100644 --- a/components/local-app-api/localapp.proto +++ b/components/local-app-api/localapp.proto @@ -12,6 +12,7 @@ import "supervisor-api/port.proto"; service LocalApp { rpc TunnelStatus(TunnelStatusRequest) returns (stream TunnelStatusResponse) {} rpc AutoTunnel(AutoTunnelRequest) returns (AutoTunnelResponse) {} + rpc ResolveSSHConnection(ResolveSSHConnectionRequest) returns (ResolveSSHConnectionResponse) {} } message TunnelStatusRequest { string instance_id = 1; @@ -31,3 +32,12 @@ message AutoTunnelRequest { bool enabled = 2; } message AutoTunnelResponse {} + +message ResolveSSHConnectionRequest { + string instance_id = 1; + string workspace_id = 2; +} +message ResolveSSHConnectionResponse { + string config_file = 1; + string host = 2; +} diff --git a/components/local-app/main.go b/components/local-app/main.go index ce98f09b11ae02..d219b9f2d613ac 100644 --- a/components/local-app/main.go +++ b/components/local-app/main.go @@ -74,8 +74,25 @@ func main() { &cli.IntFlag{ Name: "api-port", Usage: "Local App API endpoint's port", + EnvVars: []string{ + "GITPOD_LCA_API_PORT", + }, Value: 63100, }, + &cli.BoolFlag{ + Name: "auto-tunnel", + Usage: "Enable auto tunneling", + EnvVars: []string{ + "GITPOD_LCA_AUTO_TUNNEL", + }, + Value: true, + }, + &cli.StringFlag{ + Name: "auth-redirect-url", + EnvVars: []string{ + "GITPOD_LCA_AUTH_REDIRECT_URL", + }, + }, }, Commands: []*cli.Command{ { @@ -84,7 +101,7 @@ func main() { if c.Bool("mock-keyring") { keyring.MockInit() } - return run(c.String("gitpod-host"), c.String("ssh_config"), c.Int("api-port"), c.Bool("allow-cors-from-port")) + return run(c.String("gitpod-host"), c.String("ssh_config"), c.Int("api-port"), c.Bool("allow-cors-from-port"), c.Bool("auto-tunnel"), c.String("auth-redirect-url")) }, Flags: []cli.Flag{ &cli.PathFlag{ @@ -108,14 +125,14 @@ func DefaultCommand(name string) cli.ActionFunc { } } -func run(origin, sshConfig string, apiPort int, allowCORSFromPort bool) error { +func run(origin, sshConfig string, apiPort int, allowCORSFromPort bool, autoTunnel bool, authRedirectUrl string) error { logrus.WithField("ssh_config", sshConfig).Info("writing workspace ssh_config file") // Trailing slash(es) result in connection issues, so remove them preemptively origin = strings.TrimRight(origin, "/") tkn, err := auth.GetToken(origin) if errors.Is(err, keyring.ErrNotFound) { - tkn, err = auth.Login(context.Background(), auth.LoginOpts{GitpodURL: origin}) + tkn, err = auth.Login(context.Background(), auth.LoginOpts{GitpodURL: origin, RedirectURL: authRedirectUrl}) if tkn != "" { err = auth.SetToken(origin, tkn) if err != nil { @@ -146,8 +163,9 @@ func run(origin, sshConfig string, apiPort int, allowCORSFromPort bool) error { cb := bastion.CompositeCallbacks{ &logCallbacks{}, } + s := &bastion.SSHConfigWritingCallback{Path: sshConfig} if sshConfig != "" { - cb = append(cb, &bastion.SSHConfigWritingCallback{Path: sshConfig}) + cb = append(cb, s) } var b *bastion.Bastion @@ -171,8 +189,9 @@ func run(origin, sshConfig string, apiPort int, allowCORSFromPort bool) error { } b = bastion.New(client, cb) + b.EnableAutoTunnel = autoTunnel grpcServer := grpc.NewServer() - appapi.RegisterLocalAppServer(grpcServer, bastion.NewLocalAppService(b)) + appapi.RegisterLocalAppServer(grpcServer, bastion.NewLocalAppService(b, s)) allowOrigin := func(origin string) bool { // Is the origin a subdomain of the installations hostname? return hostRegex.Match([]byte(origin)) diff --git a/components/local-app/pkg/auth/auth.go b/components/local-app/pkg/auth/auth.go index 65d3b0e13baae2..4b23c29f04cc68 100644 --- a/components/local-app/pkg/auth/auth.go +++ b/components/local-app/pkg/auth/auth.go @@ -36,7 +36,8 @@ func GetToken(host string) (token string, err error) { // LoginOpts configure the login process type LoginOpts struct { - GitpodURL string + GitpodURL string + RedirectURL string } const html = ` @@ -87,7 +88,11 @@ func Login(ctx context.Context, opts LoginOpts) (token string, err error) { returnHandler := func(rw http.ResponseWriter, req *http.Request) { queryChan <- req.URL.Query() - io.WriteString(rw, html) + if opts.RedirectURL != "" { + http.Redirect(rw, req, opts.RedirectURL, http.StatusSeeOther) + } else { + io.WriteString(rw, html) + } } returnServer := &http.Server{ diff --git a/components/local-app/pkg/bastion/bastion.go b/components/local-app/pkg/bastion/bastion.go index b9ba1eb57dcb54..26e2e078819343 100644 --- a/components/local-app/pkg/bastion/bastion.go +++ b/components/local-app/pkg/bastion/bastion.go @@ -170,14 +170,19 @@ func New(client gitpod.APIInterface, cb Callbacks) *Bastion { workspaces: make(map[string]*Workspace), ctx: ctx, stop: cancel, - updates: make(chan *gitpod.WorkspaceInstance, 10), + updates: make(chan *WorkspaceUpdateRequest, 10), subscriptions: make(map[*StatusSubscription]struct{}, 10), } } +type WorkspaceUpdateRequest struct { + instance *gitpod.WorkspaceInstance + done chan *Workspace +} + type Bastion struct { id string - updates chan *gitpod.WorkspaceInstance + updates chan *WorkspaceUpdateRequest Client gitpod.APIInterface Callbacks Callbacks @@ -190,6 +195,8 @@ type Bastion struct { subscriptionsMu sync.RWMutex subscriptions map[*StatusSubscription]struct{} + + EnableAutoTunnel bool } func (b *Bastion) Run() error { @@ -221,7 +228,9 @@ func (b *Bastion) Run() error { b.FullUpdate() for u := range updates { - b.updates <- u + b.updates <- &WorkspaceUpdateRequest{ + instance: u, + } } return nil } @@ -235,23 +244,39 @@ func (b *Bastion) FullUpdate() { if ws.LatestInstance == nil { continue } - b.updates <- ws.LatestInstance + b.updates <- &WorkspaceUpdateRequest{ + instance: ws.LatestInstance, + } } } } -func (b *Bastion) Update(workspaceID string) { +func (b *Bastion) Update(workspaceID string) *Workspace { ws, err := b.Client.GetWorkspace(b.ctx, workspaceID) if err != nil { logrus.WithError(err).WithField("WorkspaceID", workspaceID).Warn("cannot get workspace") } if ws.LatestInstance == nil { - return + return nil + } + done := make(chan *Workspace) + b.updates <- &WorkspaceUpdateRequest{ + instance: ws.LatestInstance, + done: done, } - b.updates <- ws.LatestInstance + return <-done } -func (b *Bastion) handleUpdate(u *gitpod.WorkspaceInstance) { +func (b *Bastion) handleUpdate(ur *WorkspaceUpdateRequest) { + var ws *Workspace + u := ur.instance + defer func() { + if ur.done != nil { + ur.done <- ws + close(ur.done) + } + }() + b.workspacesMu.Lock() defer b.workspacesMu.Unlock() @@ -310,7 +335,7 @@ func (b *Bastion) handleUpdate(u *gitpod.WorkspaceInstance) { } } - if ws.supervisorClient != nil { + if ws.supervisorClient != nil && b.EnableAutoTunnel { go b.tunnelPorts(ws) } @@ -846,7 +871,7 @@ func (b *Bastion) AutoTunnel(instanceID string, enabled bool) { } ws.tunnelEnabled = enabled if enabled { - if ws.cancelTunnel == nil { + if ws.cancelTunnel == nil && b.EnableAutoTunnel { b.Update(ws.WorkspaceID) } } else if ws.cancelTunnel != nil { diff --git a/components/local-app/pkg/bastion/service.go b/components/local-app/pkg/bastion/service.go index f796d5154bdbeb..08338fb61d413c 100644 --- a/components/local-app/pkg/bastion/service.go +++ b/components/local-app/pkg/bastion/service.go @@ -14,12 +14,14 @@ import ( type LocalAppService struct { b *Bastion + s *SSHConfigWritingCallback api.UnimplementedLocalAppServer } -func NewLocalAppService(b *Bastion) *LocalAppService { +func NewLocalAppService(b *Bastion, s *SSHConfigWritingCallback) *LocalAppService { return &LocalAppService{ b: b, + s: s, } } @@ -61,3 +63,17 @@ func (s *LocalAppService) AutoTunnel(ctx context.Context, req *api.AutoTunnelReq s.b.AutoTunnel(req.InstanceId, req.Enabled) return &api.AutoTunnelResponse{}, nil } + +func (s *LocalAppService) ResolveSSHConnection(ctx context.Context, req *api.ResolveSSHConnectionRequest) (*api.ResolveSSHConnectionResponse, error) { + ws := s.b.Update(req.WorkspaceId) + if ws == nil || ws.InstanceID != req.InstanceId { + return nil, status.Error(codes.NotFound, "workspace not found") + } + if ws.localSSHListener == nil { + return nil, status.Error(codes.NotFound, "workspace ssh tunnel not configured") + } + return &api.ResolveSSHConnectionResponse{ + Host: ws.WorkspaceID, + ConfigFile: s.s.Path, + }, nil +}