From fe6e39e3a34dd48ea5c52717a3939ab3f7e4d87f Mon Sep 17 00:00:00 2001 From: Pudong Zheng Date: Wed, 15 Jun 2022 17:18:19 +0000 Subject: [PATCH] [workspace] support user upload ssh keys Co-authored-by: Huiwen Co-authored-by: Christian Weichel Co-authored-by: Pavel Tumik <18602811+sagor999@users.noreply.github.com> --- components/common-go/kubernetes/kubernetes.go | 3 + components/ws-manager-api/core.proto | 24 + components/ws-manager-api/go/core.pb.go | 876 +++++++++++------- components/ws-manager-api/go/core_grpc.pb.go | 38 + components/ws-manager-api/go/mock/mock.go | 35 + .../typescript/src/core_grpc_pb.d.ts | 17 + .../typescript/src/core_grpc_pb.js | 34 + .../typescript/src/core_pb.d.ts | 69 ++ .../ws-manager-api/typescript/src/core_pb.js | 562 ++++++++++- .../typescript/src/promisified-client.ts | 27 + components/ws-manager/pkg/manager/create.go | 11 + components/ws-manager/pkg/manager/manager.go | 42 + .../pkg/manager/testdata/cdwp_sshkeys.golden | 266 ++++++ .../pkg/manager/testdata/cdwp_sshkeys.json | 78 ++ components/ws-proxy/pkg/proxy/infoprovider.go | 22 +- components/ws-proxy/pkg/sshproxy/server.go | 66 +- 16 files changed, 1814 insertions(+), 356 deletions(-) create mode 100644 components/ws-manager/pkg/manager/testdata/cdwp_sshkeys.golden create mode 100644 components/ws-manager/pkg/manager/testdata/cdwp_sshkeys.json diff --git a/components/common-go/kubernetes/kubernetes.go b/components/common-go/kubernetes/kubernetes.go index ae4a2022f00ca7..62846ff12ec546 100644 --- a/components/common-go/kubernetes/kubernetes.go +++ b/components/common-go/kubernetes/kubernetes.go @@ -65,6 +65,9 @@ const ( // WorkspaceExposedPorts contains the exposed ports in the workspace WorkspaceExposedPorts = "gitpod/exposedPorts" + + // WorkspaceSSHPublicKeys contains all authorized ssh public keys that can be connected to the workspace + WorkspaceSSHPublicKeys = "gitpod.io/sshPublicKeys" ) // WorkspaceSupervisorEndpoint produces the supervisor endpoint of a workspace. diff --git a/components/ws-manager-api/core.proto b/components/ws-manager-api/core.proto index f7c6b094b99ef3..b1e534dc5bff16 100644 --- a/components/ws-manager-api/core.proto +++ b/components/ws-manager-api/core.proto @@ -43,6 +43,9 @@ service WorkspaceManager { // deleteVolumeSnapshot asks ws-manager to delete specific volume snapshot and delete source from cloud provider as well rpc DeleteVolumeSnapshot(DeleteVolumeSnapshotRequest) returns (DeleteVolumeSnapshotResponse) {} + + // UpdateSSHKey update ssh keys + rpc UpdateSSHKey(UpdateSSHKeyRequest) returns (UpdateSSHKeyResponse) {} } // MetadataFilter describes conditions for matching a set of workspaces. @@ -252,6 +255,18 @@ message BackupWorkspaceResponse { string url = 1; } +// UpdateSSHKeyRequest update ssh public key +message UpdateSSHKeyRequest { + // ID is the unique identifier of the workspace + string id = 1; + + // keys is a set of authorized_keys + repeated string keys = 2; +} + +// UpdateSSHKeyResponse is the answer to a upload ssh key request +message UpdateSSHKeyResponse {} + // WorkspaceStatus describes a workspace status message WorkspaceStatus { // ID is the unique identifier of the workspace @@ -540,6 +555,9 @@ message StartWorkspaceSpec { // volume_snapshot to use to restore PVC from, if set VolumeSnapshotInfo volume_snapshot = 14; + + // ssh_public_keys is user's uploaded ssh public keys + repeated string ssh_public_keys = 15; } // WorkspaceFeatureFlag enable non-standard behaviour in workspaces @@ -624,3 +642,9 @@ message ExposedPorts { // ports is the set of ports which ought to be exposed to the internet repeated PortSpec ports = 1; } + +// SSHPublicKeys describes the user's uploaded ssh public keys, it will be used only in annotations. +message SSHPublicKeys { + // keys is the set of ssh public key + repeated string keys = 1; +} diff --git a/components/ws-manager-api/go/core.pb.go b/components/ws-manager-api/go/core.pb.go index c268ad38b5509c..c32a4478c10f6a 100644 --- a/components/ws-manager-api/go/core.pb.go +++ b/components/ws-manager-api/go/core.pb.go @@ -1759,6 +1759,103 @@ func (x *BackupWorkspaceResponse) GetUrl() string { return "" } +// UpdateSSHKeyRequest update ssh public key +type UpdateSSHKeyRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // ID is the unique identifier of the workspace + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // keys is a set of authorized_keys + Keys []string `protobuf:"bytes,2,rep,name=keys,proto3" json:"keys,omitempty"` +} + +func (x *UpdateSSHKeyRequest) Reset() { + *x = UpdateSSHKeyRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_core_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateSSHKeyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateSSHKeyRequest) ProtoMessage() {} + +func (x *UpdateSSHKeyRequest) ProtoReflect() protoreflect.Message { + mi := &file_core_proto_msgTypes[25] + 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 UpdateSSHKeyRequest.ProtoReflect.Descriptor instead. +func (*UpdateSSHKeyRequest) Descriptor() ([]byte, []int) { + return file_core_proto_rawDescGZIP(), []int{25} +} + +func (x *UpdateSSHKeyRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *UpdateSSHKeyRequest) GetKeys() []string { + if x != nil { + return x.Keys + } + return nil +} + +// UpdateSSHKeyResponse is the answer to a upload ssh key request +type UpdateSSHKeyResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *UpdateSSHKeyResponse) Reset() { + *x = UpdateSSHKeyResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_core_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateSSHKeyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateSSHKeyResponse) ProtoMessage() {} + +func (x *UpdateSSHKeyResponse) ProtoReflect() protoreflect.Message { + mi := &file_core_proto_msgTypes[26] + 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 UpdateSSHKeyResponse.ProtoReflect.Descriptor instead. +func (*UpdateSSHKeyResponse) Descriptor() ([]byte, []int) { + return file_core_proto_rawDescGZIP(), []int{26} +} + // WorkspaceStatus describes a workspace status type WorkspaceStatus struct { state protoimpl.MessageState @@ -1796,7 +1893,7 @@ type WorkspaceStatus struct { func (x *WorkspaceStatus) Reset() { *x = WorkspaceStatus{} if protoimpl.UnsafeEnabled { - mi := &file_core_proto_msgTypes[25] + mi := &file_core_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1809,7 +1906,7 @@ func (x *WorkspaceStatus) String() string { func (*WorkspaceStatus) ProtoMessage() {} func (x *WorkspaceStatus) ProtoReflect() protoreflect.Message { - mi := &file_core_proto_msgTypes[25] + mi := &file_core_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1822,7 +1919,7 @@ func (x *WorkspaceStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkspaceStatus.ProtoReflect.Descriptor instead. func (*WorkspaceStatus) Descriptor() ([]byte, []int) { - return file_core_proto_rawDescGZIP(), []int{25} + return file_core_proto_rawDescGZIP(), []int{27} } func (x *WorkspaceStatus) GetId() string { @@ -1912,7 +2009,7 @@ type IDEImage struct { func (x *IDEImage) Reset() { *x = IDEImage{} if protoimpl.UnsafeEnabled { - mi := &file_core_proto_msgTypes[26] + mi := &file_core_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1925,7 +2022,7 @@ func (x *IDEImage) String() string { func (*IDEImage) ProtoMessage() {} func (x *IDEImage) ProtoReflect() protoreflect.Message { - mi := &file_core_proto_msgTypes[26] + mi := &file_core_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1938,7 +2035,7 @@ func (x *IDEImage) ProtoReflect() protoreflect.Message { // Deprecated: Use IDEImage.ProtoReflect.Descriptor instead. func (*IDEImage) Descriptor() ([]byte, []int) { - return file_core_proto_rawDescGZIP(), []int{26} + return file_core_proto_rawDescGZIP(), []int{28} } func (x *IDEImage) GetWebRef() string { @@ -1992,7 +2089,7 @@ type WorkspaceSpec struct { func (x *WorkspaceSpec) Reset() { *x = WorkspaceSpec{} if protoimpl.UnsafeEnabled { - mi := &file_core_proto_msgTypes[27] + mi := &file_core_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2005,7 +2102,7 @@ func (x *WorkspaceSpec) String() string { func (*WorkspaceSpec) ProtoMessage() {} func (x *WorkspaceSpec) ProtoReflect() protoreflect.Message { - mi := &file_core_proto_msgTypes[27] + mi := &file_core_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2018,7 +2115,7 @@ func (x *WorkspaceSpec) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkspaceSpec.ProtoReflect.Descriptor instead. func (*WorkspaceSpec) Descriptor() ([]byte, []int) { - return file_core_proto_rawDescGZIP(), []int{27} + return file_core_proto_rawDescGZIP(), []int{29} } func (x *WorkspaceSpec) GetWorkspaceImage() string { @@ -2101,7 +2198,7 @@ type PortSpec struct { func (x *PortSpec) Reset() { *x = PortSpec{} if protoimpl.UnsafeEnabled { - mi := &file_core_proto_msgTypes[28] + mi := &file_core_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2114,7 +2211,7 @@ func (x *PortSpec) String() string { func (*PortSpec) ProtoMessage() {} func (x *PortSpec) ProtoReflect() protoreflect.Message { - mi := &file_core_proto_msgTypes[28] + mi := &file_core_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2127,7 +2224,7 @@ func (x *PortSpec) ProtoReflect() protoreflect.Message { // Deprecated: Use PortSpec.ProtoReflect.Descriptor instead. func (*PortSpec) Descriptor() ([]byte, []int) { - return file_core_proto_rawDescGZIP(), []int{28} + return file_core_proto_rawDescGZIP(), []int{30} } func (x *PortSpec) GetPort() uint32 { @@ -2166,7 +2263,7 @@ type VolumeSnapshotInfo struct { func (x *VolumeSnapshotInfo) Reset() { *x = VolumeSnapshotInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_proto_msgTypes[29] + mi := &file_core_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2179,7 +2276,7 @@ func (x *VolumeSnapshotInfo) String() string { func (*VolumeSnapshotInfo) ProtoMessage() {} func (x *VolumeSnapshotInfo) ProtoReflect() protoreflect.Message { - mi := &file_core_proto_msgTypes[29] + mi := &file_core_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2192,7 +2289,7 @@ func (x *VolumeSnapshotInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeSnapshotInfo.ProtoReflect.Descriptor instead. func (*VolumeSnapshotInfo) Descriptor() ([]byte, []int) { - return file_core_proto_rawDescGZIP(), []int{29} + return file_core_proto_rawDescGZIP(), []int{31} } func (x *VolumeSnapshotInfo) GetVolumeSnapshotName() string { @@ -2244,7 +2341,7 @@ type WorkspaceConditions struct { func (x *WorkspaceConditions) Reset() { *x = WorkspaceConditions{} if protoimpl.UnsafeEnabled { - mi := &file_core_proto_msgTypes[30] + mi := &file_core_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2257,7 +2354,7 @@ func (x *WorkspaceConditions) String() string { func (*WorkspaceConditions) ProtoMessage() {} func (x *WorkspaceConditions) ProtoReflect() protoreflect.Message { - mi := &file_core_proto_msgTypes[30] + mi := &file_core_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2270,7 +2367,7 @@ func (x *WorkspaceConditions) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkspaceConditions.ProtoReflect.Descriptor instead. func (*WorkspaceConditions) Descriptor() ([]byte, []int) { - return file_core_proto_rawDescGZIP(), []int{30} + return file_core_proto_rawDescGZIP(), []int{32} } func (x *WorkspaceConditions) GetFailed() string { @@ -2374,7 +2471,7 @@ type WorkspaceMetadata struct { func (x *WorkspaceMetadata) Reset() { *x = WorkspaceMetadata{} if protoimpl.UnsafeEnabled { - mi := &file_core_proto_msgTypes[31] + mi := &file_core_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2387,7 +2484,7 @@ func (x *WorkspaceMetadata) String() string { func (*WorkspaceMetadata) ProtoMessage() {} func (x *WorkspaceMetadata) ProtoReflect() protoreflect.Message { - mi := &file_core_proto_msgTypes[31] + mi := &file_core_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2400,7 +2497,7 @@ func (x *WorkspaceMetadata) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkspaceMetadata.ProtoReflect.Descriptor instead. func (*WorkspaceMetadata) Descriptor() ([]byte, []int) { - return file_core_proto_rawDescGZIP(), []int{31} + return file_core_proto_rawDescGZIP(), []int{33} } func (x *WorkspaceMetadata) GetOwner() string { @@ -2464,7 +2561,7 @@ type WorkspaceRuntimeInfo struct { func (x *WorkspaceRuntimeInfo) Reset() { *x = WorkspaceRuntimeInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_proto_msgTypes[32] + mi := &file_core_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2477,7 +2574,7 @@ func (x *WorkspaceRuntimeInfo) String() string { func (*WorkspaceRuntimeInfo) ProtoMessage() {} func (x *WorkspaceRuntimeInfo) ProtoReflect() protoreflect.Message { - mi := &file_core_proto_msgTypes[32] + mi := &file_core_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2490,7 +2587,7 @@ func (x *WorkspaceRuntimeInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkspaceRuntimeInfo.ProtoReflect.Descriptor instead. func (*WorkspaceRuntimeInfo) Descriptor() ([]byte, []int) { - return file_core_proto_rawDescGZIP(), []int{32} + return file_core_proto_rawDescGZIP(), []int{34} } func (x *WorkspaceRuntimeInfo) GetNodeName() string { @@ -2530,7 +2627,7 @@ type WorkspaceAuthentication struct { func (x *WorkspaceAuthentication) Reset() { *x = WorkspaceAuthentication{} if protoimpl.UnsafeEnabled { - mi := &file_core_proto_msgTypes[33] + mi := &file_core_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2543,7 +2640,7 @@ func (x *WorkspaceAuthentication) String() string { func (*WorkspaceAuthentication) ProtoMessage() {} func (x *WorkspaceAuthentication) ProtoReflect() protoreflect.Message { - mi := &file_core_proto_msgTypes[33] + mi := &file_core_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2556,7 +2653,7 @@ func (x *WorkspaceAuthentication) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkspaceAuthentication.ProtoReflect.Descriptor instead. func (*WorkspaceAuthentication) Descriptor() ([]byte, []int) { - return file_core_proto_rawDescGZIP(), []int{33} + return file_core_proto_rawDescGZIP(), []int{35} } func (x *WorkspaceAuthentication) GetAdmission() AdmissionLevel { @@ -2606,12 +2703,14 @@ type StartWorkspaceSpec struct { Class string `protobuf:"bytes,13,opt,name=class,proto3" json:"class,omitempty"` // volume_snapshot to use to restore PVC from, if set VolumeSnapshot *VolumeSnapshotInfo `protobuf:"bytes,14,opt,name=volume_snapshot,json=volumeSnapshot,proto3" json:"volume_snapshot,omitempty"` + // ssh_public_keys is user's uploaded ssh public keys + SshPublicKeys []string `protobuf:"bytes,15,rep,name=ssh_public_keys,json=sshPublicKeys,proto3" json:"ssh_public_keys,omitempty"` } func (x *StartWorkspaceSpec) Reset() { *x = StartWorkspaceSpec{} if protoimpl.UnsafeEnabled { - mi := &file_core_proto_msgTypes[34] + mi := &file_core_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2624,7 +2723,7 @@ func (x *StartWorkspaceSpec) String() string { func (*StartWorkspaceSpec) ProtoMessage() {} func (x *StartWorkspaceSpec) ProtoReflect() protoreflect.Message { - mi := &file_core_proto_msgTypes[34] + mi := &file_core_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2637,7 +2736,7 @@ func (x *StartWorkspaceSpec) ProtoReflect() protoreflect.Message { // Deprecated: Use StartWorkspaceSpec.ProtoReflect.Descriptor instead. func (*StartWorkspaceSpec) Descriptor() ([]byte, []int) { - return file_core_proto_rawDescGZIP(), []int{34} + return file_core_proto_rawDescGZIP(), []int{36} } func (x *StartWorkspaceSpec) GetWorkspaceImage() string { @@ -2731,6 +2830,13 @@ func (x *StartWorkspaceSpec) GetVolumeSnapshot() *VolumeSnapshotInfo { return nil } +func (x *StartWorkspaceSpec) GetSshPublicKeys() []string { + if x != nil { + return x.SshPublicKeys + } + return nil +} + // GitSpec configures the Git available within the workspace type GitSpec struct { state protoimpl.MessageState @@ -2746,7 +2852,7 @@ type GitSpec struct { func (x *GitSpec) Reset() { *x = GitSpec{} if protoimpl.UnsafeEnabled { - mi := &file_core_proto_msgTypes[35] + mi := &file_core_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2759,7 +2865,7 @@ func (x *GitSpec) String() string { func (*GitSpec) ProtoMessage() {} func (x *GitSpec) ProtoReflect() protoreflect.Message { - mi := &file_core_proto_msgTypes[35] + mi := &file_core_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2772,7 +2878,7 @@ func (x *GitSpec) ProtoReflect() protoreflect.Message { // Deprecated: Use GitSpec.ProtoReflect.Descriptor instead. func (*GitSpec) Descriptor() ([]byte, []int) { - return file_core_proto_rawDescGZIP(), []int{35} + return file_core_proto_rawDescGZIP(), []int{37} } func (x *GitSpec) GetUsername() string { @@ -2806,7 +2912,7 @@ type EnvironmentVariable struct { func (x *EnvironmentVariable) Reset() { *x = EnvironmentVariable{} if protoimpl.UnsafeEnabled { - mi := &file_core_proto_msgTypes[36] + mi := &file_core_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2819,7 +2925,7 @@ func (x *EnvironmentVariable) String() string { func (*EnvironmentVariable) ProtoMessage() {} func (x *EnvironmentVariable) ProtoReflect() protoreflect.Message { - mi := &file_core_proto_msgTypes[36] + mi := &file_core_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2832,7 +2938,7 @@ func (x *EnvironmentVariable) ProtoReflect() protoreflect.Message { // Deprecated: Use EnvironmentVariable.ProtoReflect.Descriptor instead. func (*EnvironmentVariable) Descriptor() ([]byte, []int) { - return file_core_proto_rawDescGZIP(), []int{36} + return file_core_proto_rawDescGZIP(), []int{38} } func (x *EnvironmentVariable) GetName() string { @@ -2869,7 +2975,7 @@ type ExposedPorts struct { func (x *ExposedPorts) Reset() { *x = ExposedPorts{} if protoimpl.UnsafeEnabled { - mi := &file_core_proto_msgTypes[37] + mi := &file_core_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2882,7 +2988,7 @@ func (x *ExposedPorts) String() string { func (*ExposedPorts) ProtoMessage() {} func (x *ExposedPorts) ProtoReflect() protoreflect.Message { - mi := &file_core_proto_msgTypes[37] + mi := &file_core_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2895,7 +3001,7 @@ func (x *ExposedPorts) ProtoReflect() protoreflect.Message { // Deprecated: Use ExposedPorts.ProtoReflect.Descriptor instead. func (*ExposedPorts) Descriptor() ([]byte, []int) { - return file_core_proto_rawDescGZIP(), []int{37} + return file_core_proto_rawDescGZIP(), []int{39} } func (x *ExposedPorts) GetPorts() []*PortSpec { @@ -2905,6 +3011,55 @@ func (x *ExposedPorts) GetPorts() []*PortSpec { return nil } +// SSHPublicKeys describes the user's uploaded ssh public keys, it will be used only in annotations. +type SSHPublicKeys struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // keys is the set of ssh public key + Keys []string `protobuf:"bytes,1,rep,name=keys,proto3" json:"keys,omitempty"` +} + +func (x *SSHPublicKeys) Reset() { + *x = SSHPublicKeys{} + if protoimpl.UnsafeEnabled { + mi := &file_core_proto_msgTypes[40] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SSHPublicKeys) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SSHPublicKeys) ProtoMessage() {} + +func (x *SSHPublicKeys) ProtoReflect() protoreflect.Message { + mi := &file_core_proto_msgTypes[40] + 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 SSHPublicKeys.ProtoReflect.Descriptor instead. +func (*SSHPublicKeys) Descriptor() ([]byte, []int) { + return file_core_proto_rawDescGZIP(), []int{40} +} + +func (x *SSHPublicKeys) GetKeys() []string { + if x != nil { + return x.Keys + } + return nil +} + type EnvironmentVariable_SecretKeyRef struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2917,7 +3072,7 @@ type EnvironmentVariable_SecretKeyRef struct { func (x *EnvironmentVariable_SecretKeyRef) Reset() { *x = EnvironmentVariable_SecretKeyRef{} if protoimpl.UnsafeEnabled { - mi := &file_core_proto_msgTypes[41] + mi := &file_core_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2930,7 +3085,7 @@ func (x *EnvironmentVariable_SecretKeyRef) String() string { func (*EnvironmentVariable_SecretKeyRef) ProtoMessage() {} func (x *EnvironmentVariable_SecretKeyRef) ProtoReflect() protoreflect.Message { - mi := &file_core_proto_msgTypes[41] + mi := &file_core_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2943,7 +3098,7 @@ func (x *EnvironmentVariable_SecretKeyRef) ProtoReflect() protoreflect.Message { // Deprecated: Use EnvironmentVariable_SecretKeyRef.ProtoReflect.Descriptor instead. func (*EnvironmentVariable_SecretKeyRef) Descriptor() ([]byte, []int) { - return file_core_proto_rawDescGZIP(), []int{36, 0} + return file_core_proto_rawDescGZIP(), []int{38, 0} } func (x *EnvironmentVariable_SecretKeyRef) GetSecretName() string { @@ -3094,215 +3249,225 @@ var file_core_proto_rawDesc = []byte{ 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x2b, 0x0a, 0x17, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0xc5, 0x03, 0x0a, 0x0f, 0x57, 0x6f, 0x72, 0x6b, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x57, 0x6f, 0x72, - 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, - 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x28, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x57, - 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, - 0x65, 0x63, 0x12, 0x2b, 0x0a, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x15, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x50, 0x68, 0x61, 0x73, 0x65, 0x52, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x12, - 0x3a, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, - 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2d, 0x0a, 0x04, 0x72, 0x65, 0x70, 0x6f, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x2e, 0x47, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x04, - 0x72, 0x65, 0x70, 0x6f, 0x12, 0x35, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x57, 0x6f, - 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x32, 0x0a, 0x04, 0x61, - 0x75, 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x77, 0x73, 0x6d, 0x61, - 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x65, - 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x61, 0x75, 0x74, 0x68, 0x22, - 0x6b, 0x0a, 0x08, 0x49, 0x44, 0x45, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x77, - 0x65, 0x62, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x77, 0x65, - 0x62, 0x52, 0x65, 0x66, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x5f, - 0x72, 0x65, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x73, 0x6b, 0x74, - 0x6f, 0x70, 0x52, 0x65, 0x66, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, - 0x73, 0x6f, 0x72, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, - 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x52, 0x65, 0x66, 0x22, 0xd6, 0x02, 0x0a, - 0x0d, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x27, - 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x6d, 0x61, 0x67, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x64, 0x65, 0x70, 0x72, 0x65, - 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x64, 0x65, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, - 0x64, 0x49, 0x64, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x65, 0x61, - 0x64, 0x6c, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x68, 0x65, 0x61, - 0x64, 0x6c, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x34, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x6f, 0x73, - 0x65, 0x64, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, - 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, - 0x0c, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x28, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x77, 0x73, - 0x6d, 0x61, 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x79, 0x70, - 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, - 0x75, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, - 0x74, 0x12, 0x2c, 0x0a, 0x09, 0x69, 0x64, 0x65, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x49, 0x44, 0x45, - 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x08, 0x69, 0x64, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, - 0x14, 0x0a, 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x6d, 0x0a, 0x08, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x70, 0x65, - 0x63, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x35, 0x0a, 0x0a, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, - 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x77, 0x73, 0x6d, 0x61, - 0x6e, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x56, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, - 0x52, 0x0a, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x4a, 0x04, - 0x08, 0x02, 0x10, 0x03, 0x22, 0x7c, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6e, - 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x30, 0x0a, 0x14, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x34, 0x0a, 0x16, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, - 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x48, 0x61, 0x6e, 0x64, - 0x6c, 0x65, 0x22, 0x97, 0x05, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x61, - 0x69, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x61, 0x69, 0x6c, - 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x44, 0x0a, 0x0e, - 0x70, 0x75, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x18, 0x03, + 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x39, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x53, 0x53, 0x48, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, + 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x65, + 0x79, 0x73, 0x22, 0x16, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x53, 0x48, 0x4b, + 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xc5, 0x03, 0x0a, 0x0f, 0x57, + 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x25, + 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, + 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x28, 0x0a, 0x04, 0x73, + 0x70, 0x65, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x77, 0x73, 0x6d, 0x61, + 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x52, + 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x2b, 0x0a, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x57, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x68, 0x61, 0x73, 0x65, 0x52, 0x05, 0x70, 0x68, 0x61, + 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x57, + 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x18, + 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2d, 0x0a, 0x04, 0x72, 0x65, 0x70, 0x6f, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x47, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x04, 0x72, 0x65, 0x70, 0x6f, 0x12, 0x35, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, + 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x32, + 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x77, + 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x75, + 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x61, 0x75, + 0x74, 0x68, 0x22, 0x6b, 0x0a, 0x08, 0x49, 0x44, 0x45, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x17, + 0x0a, 0x07, 0x77, 0x65, 0x62, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x77, 0x65, 0x62, 0x52, 0x65, 0x66, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x6b, 0x74, + 0x6f, 0x70, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, + 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x66, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x75, 0x70, 0x65, + 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x52, 0x65, 0x66, 0x22, + 0xd6, 0x02, 0x0a, 0x0d, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x70, 0x65, + 0x63, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, + 0x6d, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x64, 0x65, + 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x64, 0x65, 0x5f, 0x69, 0x6d, 0x61, + 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, + 0x61, 0x74, 0x65, 0x64, 0x49, 0x64, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x68, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, + 0x68, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x34, 0x0a, 0x0d, 0x65, 0x78, + 0x70, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x0f, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x70, + 0x65, 0x63, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x73, + 0x12, 0x28, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, + 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, + 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x69, 0x6d, + 0x65, 0x6f, 0x75, 0x74, 0x12, 0x2c, 0x0a, 0x09, 0x69, 0x64, 0x65, 0x5f, 0x69, 0x6d, 0x61, 0x67, + 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, + 0x49, 0x44, 0x45, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x08, 0x69, 0x64, 0x65, 0x49, 0x6d, 0x61, + 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x6d, 0x0a, 0x08, 0x50, 0x6f, 0x72, 0x74, + 0x53, 0x70, 0x65, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x35, 0x0a, 0x0a, 0x76, 0x69, 0x73, 0x69, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x77, + 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x56, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, + 0x69, 0x74, 0x79, 0x52, 0x0a, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, + 0x6c, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0x7c, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x30, 0x0a, + 0x14, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x34, 0x0a, 0x16, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, + 0x6f, 0x74, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x14, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x48, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x22, 0x97, 0x05, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x16, 0x0a, + 0x06, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, + 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, + 0x44, 0x0a, 0x0e, 0x70, 0x75, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, + 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x42, 0x6f, 0x6f, 0x6c, 0x52, 0x0d, 0x70, 0x75, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x49, + 0x6d, 0x61, 0x67, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, + 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, + 0x74, 0x12, 0x51, 0x0a, 0x15, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, + 0x70, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x1d, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6f, 0x6f, 0x6c, 0x52, + 0x13, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x43, 0x6f, 0x6d, 0x70, + 0x6c, 0x65, 0x74, 0x65, 0x12, 0x39, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x65, 0x64, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x57, + 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x42, 0x6f, 0x6f, 0x6c, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x65, 0x64, 0x12, + 0x49, 0x0a, 0x11, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x72, + 0x65, 0x61, 0x64, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x77, 0x73, 0x6d, + 0x61, 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x64, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6f, 0x6f, 0x6c, 0x52, 0x0f, 0x6e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x4e, 0x6f, 0x74, 0x52, 0x65, 0x61, 0x64, 0x79, 0x12, 0x4a, 0x0a, 0x13, 0x66, 0x69, + 0x72, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, + 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x11, 0x66, 0x69, 0x72, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, + 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x68, 0x65, 0x61, 0x64, 0x6c, 0x65, + 0x73, 0x73, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x68, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x54, 0x61, + 0x73, 0x6b, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x4b, 0x0a, 0x12, 0x73, 0x74, 0x6f, 0x70, + 0x70, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x42, - 0x6f, 0x6f, 0x6c, 0x52, 0x0d, 0x70, 0x75, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x49, 0x6d, 0x61, 0x67, - 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x51, - 0x0a, 0x15, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x63, - 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, - 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, - 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6f, 0x6f, 0x6c, 0x52, 0x13, 0x66, 0x69, - 0x6e, 0x61, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, - 0x65, 0x12, 0x39, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x65, 0x64, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6f, - 0x6f, 0x6c, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x65, 0x64, 0x12, 0x49, 0x0a, 0x11, - 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x72, 0x65, 0x61, 0x64, - 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, - 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x42, 0x6f, 0x6f, 0x6c, 0x52, 0x0f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4e, - 0x6f, 0x74, 0x52, 0x65, 0x61, 0x64, 0x79, 0x12, 0x4a, 0x0a, 0x13, 0x66, 0x69, 0x72, 0x73, 0x74, - 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x11, 0x66, 0x69, 0x72, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, - 0x69, 0x74, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x68, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x5f, - 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x12, 0x68, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x54, 0x61, 0x73, 0x6b, 0x46, - 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x4b, 0x0a, 0x12, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, - 0x5f, 0x62, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x1d, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6f, 0x6f, 0x6c, - 0x52, 0x10, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x42, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x6e, 0x61, - 0x70, 0x73, 0x68, 0x6f, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x77, 0x73, - 0x6d, 0x61, 0x6e, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, - 0x6f, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6e, - 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0xd7, 0x02, 0x0a, - 0x11, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x17, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x61, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x61, 0x49, - 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x4b, 0x0a, 0x0b, - 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x29, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, 0x6e, - 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x17, 0x0a, 0x04, 0x74, 0x65, 0x61, - 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x74, 0x65, 0x61, 0x6d, 0x88, - 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x88, 0x01, - 0x01, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x74, 0x65, 0x61, 0x6d, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x67, 0x0a, 0x14, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1b, - 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x70, - 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, - 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, - 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x70, 0x22, - 0x6f, 0x0a, 0x17, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, - 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x09, 0x61, 0x64, - 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, - 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, - 0x65, 0x76, 0x65, 0x6c, 0x52, 0x09, 0x61, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, - 0x1f, 0x0a, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x22, 0x84, 0x05, 0x0a, 0x12, 0x53, 0x74, 0x61, 0x72, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, - 0x12, 0x30, 0x0a, 0x14, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x69, - 0x64, 0x65, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, - 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x49, 0x64, 0x65, 0x49, 0x6d, 0x61, - 0x67, 0x65, 0x12, 0x40, 0x0a, 0x0d, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x66, 0x6c, - 0x61, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x77, 0x73, 0x6d, 0x61, - 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x52, 0x0c, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x46, - 0x6c, 0x61, 0x67, 0x73, 0x12, 0x46, 0x0a, 0x0b, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, - 0x7a, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x72, 0x52, - 0x0b, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x72, 0x12, 0x25, 0x0a, 0x05, - 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x77, 0x73, - 0x6d, 0x61, 0x6e, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x05, 0x70, 0x6f, - 0x72, 0x74, 0x73, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x76, 0x76, 0x61, 0x72, 0x73, 0x18, 0x06, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x45, 0x6e, 0x76, - 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, - 0x52, 0x07, 0x65, 0x6e, 0x76, 0x76, 0x61, 0x72, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x77, 0x6f, 0x72, - 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x03, 0x67, 0x69, 0x74, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x47, 0x69, - 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x03, 0x67, 0x69, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, - 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x69, 0x6d, - 0x65, 0x6f, 0x75, 0x74, 0x12, 0x33, 0x0a, 0x09, 0x61, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, - 0x41, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x09, - 0x61, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x09, 0x69, 0x64, 0x65, - 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x77, - 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x49, 0x44, 0x45, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x08, 0x69, - 0x64, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, - 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x42, 0x0a, - 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, - 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, - 0x74, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x22, 0x3b, 0x0a, 0x07, 0x47, 0x69, 0x74, 0x53, 0x70, - 0x65, 0x63, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, - 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, - 0x6d, 0x61, 0x69, 0x6c, 0x22, 0xc3, 0x01, 0x0a, 0x13, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, - 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3f, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x45, - 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, - 0x6c, 0x65, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x66, 0x52, - 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x1a, 0x41, 0x0a, 0x0c, 0x53, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x66, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x35, 0x0a, 0x0c, 0x45, 0x78, - 0x70, 0x6f, 0x73, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x05, 0x70, 0x6f, - 0x72, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x77, 0x73, 0x6d, 0x61, - 0x6e, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x05, 0x70, 0x6f, 0x72, 0x74, + 0x6f, 0x6f, 0x6c, 0x52, 0x10, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x42, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, + 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6e, 0x61, + 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, + 0xd7, 0x02, 0x0a, 0x11, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x17, 0x0a, 0x07, 0x6d, + 0x65, 0x74, 0x61, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, + 0x74, 0x61, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, + 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, + 0x4b, 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x57, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x41, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x17, 0x0a, 0x04, + 0x74, 0x65, 0x61, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x74, 0x65, + 0x61, 0x6d, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x88, 0x01, 0x01, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x74, 0x65, 0x61, 0x6d, 0x42, 0x0a, 0x0a, + 0x08, 0x5f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x67, 0x0a, 0x14, 0x57, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, + 0x0a, 0x08, 0x70, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x70, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, + 0x65, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, + 0x49, 0x70, 0x22, 0x6f, 0x0a, 0x17, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, + 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, + 0x09, 0x61, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x15, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x09, 0x61, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x22, 0xac, 0x05, 0x0a, 0x12, 0x53, 0x74, 0x61, 0x72, 0x74, 0x57, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x6f, + 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x6d, + 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, + 0x64, 0x5f, 0x69, 0x64, 0x65, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x12, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x49, 0x64, 0x65, + 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x40, 0x0a, 0x0d, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x77, + 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x46, 0x65, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x52, 0x0c, 0x66, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x46, 0x0a, 0x0b, 0x69, 0x6e, 0x69, 0x74, 0x69, + 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x57, 0x6f, + 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, + 0x65, 0x72, 0x52, 0x0b, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x72, 0x12, + 0x25, 0x0a, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, + 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, + 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x76, 0x76, 0x61, 0x72, + 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, + 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, + 0x62, 0x6c, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x76, 0x76, 0x61, 0x72, 0x73, 0x12, 0x2d, 0x0a, 0x12, + 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x03, 0x67, + 0x69, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, + 0x2e, 0x47, 0x69, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x03, 0x67, 0x69, 0x74, 0x12, 0x18, 0x0a, + 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x33, 0x0a, 0x09, 0x61, 0x64, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x77, 0x73, 0x6d, + 0x61, 0x6e, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x65, 0x76, 0x65, + 0x6c, 0x52, 0x09, 0x61, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x09, + 0x69, 0x64, 0x65, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x0f, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x49, 0x44, 0x45, 0x49, 0x6d, 0x61, 0x67, 0x65, + 0x52, 0x08, 0x69, 0x64, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6c, + 0x61, 0x73, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, + 0x12, 0x42, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, + 0x68, 0x6f, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x77, 0x73, 0x6d, 0x61, + 0x6e, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6e, 0x61, 0x70, + 0x73, 0x68, 0x6f, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x73, 0x68, 0x5f, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x73, + 0x73, 0x68, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x4a, 0x04, 0x08, 0x07, + 0x10, 0x08, 0x22, 0x3b, 0x0a, 0x07, 0x47, 0x69, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x1a, 0x0a, + 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, + 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x22, + 0xc3, 0x01, 0x0a, 0x13, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x56, + 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x12, 0x3f, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x27, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, + 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x53, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x66, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x1a, 0x41, 0x0a, 0x0c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, + 0x65, 0x66, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x35, 0x0a, 0x0c, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, + 0x50, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x50, 0x6f, 0x72, + 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x22, 0x23, 0x0a, 0x0d, + 0x53, 0x53, 0x48, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x12, 0x0a, + 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x2a, 0x34, 0x0a, 0x13, 0x53, 0x74, 0x6f, 0x70, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x4c, 0x59, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x49, 0x4d, 0x4d, 0x45, 0x44, 0x49, @@ -3340,7 +3505,7 @@ var file_core_proto_rawDesc = []byte{ 0x41, 0x52, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x52, 0x45, 0x42, 0x55, 0x49, 0x4c, 0x44, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x52, 0x4f, 0x42, 0x45, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x42, 0x55, 0x49, 0x4c, 0x44, 0x10, 0x04, 0x22, 0x04, 0x08, - 0x03, 0x10, 0x03, 0x32, 0xc8, 0x07, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x03, 0x10, 0x03, 0x32, 0x93, 0x08, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x4c, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, @@ -3400,11 +3565,15 @@ var file_core_proto_rawDesc = []byte{ 0x74, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6e, 0x61, 0x70, - 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x2c, - 0x5a, 0x2a, 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, 0x77, 0x73, - 0x2d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x49, + 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x53, 0x48, 0x4b, 0x65, 0x79, 0x12, 0x1a, + 0x2e, 0x77, 0x73, 0x6d, 0x61, 0x6e, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x53, 0x48, + 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x77, 0x73, 0x6d, + 0x61, 0x6e, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x53, 0x48, 0x4b, 0x65, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x2c, 0x5a, 0x2a, 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, 0x77, 0x73, 0x2d, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -3420,7 +3589,7 @@ func file_core_proto_rawDescGZIP() []byte { } var file_core_proto_enumTypes = make([]protoimpl.EnumInfo, 7) -var file_core_proto_msgTypes = make([]protoimpl.MessageInfo, 42) +var file_core_proto_msgTypes = make([]protoimpl.MessageInfo, 45) var file_core_proto_goTypes = []interface{}{ (StopWorkspacePolicy)(0), // 0: wsman.StopWorkspacePolicy (AdmissionLevel)(0), // 1: wsman.AdmissionLevel @@ -3454,72 +3623,75 @@ var file_core_proto_goTypes = []interface{}{ (*DeleteVolumeSnapshotResponse)(nil), // 29: wsman.DeleteVolumeSnapshotResponse (*BackupWorkspaceRequest)(nil), // 30: wsman.BackupWorkspaceRequest (*BackupWorkspaceResponse)(nil), // 31: wsman.BackupWorkspaceResponse - (*WorkspaceStatus)(nil), // 32: wsman.WorkspaceStatus - (*IDEImage)(nil), // 33: wsman.IDEImage - (*WorkspaceSpec)(nil), // 34: wsman.WorkspaceSpec - (*PortSpec)(nil), // 35: wsman.PortSpec - (*VolumeSnapshotInfo)(nil), // 36: wsman.VolumeSnapshotInfo - (*WorkspaceConditions)(nil), // 37: wsman.WorkspaceConditions - (*WorkspaceMetadata)(nil), // 38: wsman.WorkspaceMetadata - (*WorkspaceRuntimeInfo)(nil), // 39: wsman.WorkspaceRuntimeInfo - (*WorkspaceAuthentication)(nil), // 40: wsman.WorkspaceAuthentication - (*StartWorkspaceSpec)(nil), // 41: wsman.StartWorkspaceSpec - (*GitSpec)(nil), // 42: wsman.GitSpec - (*EnvironmentVariable)(nil), // 43: wsman.EnvironmentVariable - (*ExposedPorts)(nil), // 44: wsman.ExposedPorts - nil, // 45: wsman.MetadataFilter.AnnotationsEntry - nil, // 46: wsman.SubscribeResponse.HeaderEntry - nil, // 47: wsman.WorkspaceMetadata.AnnotationsEntry - (*EnvironmentVariable_SecretKeyRef)(nil), // 48: wsman.EnvironmentVariable.SecretKeyRef - (*api.GitStatus)(nil), // 49: contentservice.GitStatus - (*timestamppb.Timestamp)(nil), // 50: google.protobuf.Timestamp - (*api.WorkspaceInitializer)(nil), // 51: contentservice.WorkspaceInitializer + (*UpdateSSHKeyRequest)(nil), // 32: wsman.UpdateSSHKeyRequest + (*UpdateSSHKeyResponse)(nil), // 33: wsman.UpdateSSHKeyResponse + (*WorkspaceStatus)(nil), // 34: wsman.WorkspaceStatus + (*IDEImage)(nil), // 35: wsman.IDEImage + (*WorkspaceSpec)(nil), // 36: wsman.WorkspaceSpec + (*PortSpec)(nil), // 37: wsman.PortSpec + (*VolumeSnapshotInfo)(nil), // 38: wsman.VolumeSnapshotInfo + (*WorkspaceConditions)(nil), // 39: wsman.WorkspaceConditions + (*WorkspaceMetadata)(nil), // 40: wsman.WorkspaceMetadata + (*WorkspaceRuntimeInfo)(nil), // 41: wsman.WorkspaceRuntimeInfo + (*WorkspaceAuthentication)(nil), // 42: wsman.WorkspaceAuthentication + (*StartWorkspaceSpec)(nil), // 43: wsman.StartWorkspaceSpec + (*GitSpec)(nil), // 44: wsman.GitSpec + (*EnvironmentVariable)(nil), // 45: wsman.EnvironmentVariable + (*ExposedPorts)(nil), // 46: wsman.ExposedPorts + (*SSHPublicKeys)(nil), // 47: wsman.SSHPublicKeys + nil, // 48: wsman.MetadataFilter.AnnotationsEntry + nil, // 49: wsman.SubscribeResponse.HeaderEntry + nil, // 50: wsman.WorkspaceMetadata.AnnotationsEntry + (*EnvironmentVariable_SecretKeyRef)(nil), // 51: wsman.EnvironmentVariable.SecretKeyRef + (*api.GitStatus)(nil), // 52: contentservice.GitStatus + (*timestamppb.Timestamp)(nil), // 53: google.protobuf.Timestamp + (*api.WorkspaceInitializer)(nil), // 54: contentservice.WorkspaceInitializer } var file_core_proto_depIdxs = []int32{ - 45, // 0: wsman.MetadataFilter.annotations:type_name -> wsman.MetadataFilter.AnnotationsEntry + 48, // 0: wsman.MetadataFilter.annotations:type_name -> wsman.MetadataFilter.AnnotationsEntry 7, // 1: wsman.GetWorkspacesRequest.must_match:type_name -> wsman.MetadataFilter - 32, // 2: wsman.GetWorkspacesResponse.status:type_name -> wsman.WorkspaceStatus - 38, // 3: wsman.StartWorkspaceRequest.metadata:type_name -> wsman.WorkspaceMetadata - 41, // 4: wsman.StartWorkspaceRequest.spec:type_name -> wsman.StartWorkspaceSpec + 34, // 2: wsman.GetWorkspacesResponse.status:type_name -> wsman.WorkspaceStatus + 40, // 3: wsman.StartWorkspaceRequest.metadata:type_name -> wsman.WorkspaceMetadata + 43, // 4: wsman.StartWorkspaceRequest.spec:type_name -> wsman.StartWorkspaceSpec 6, // 5: wsman.StartWorkspaceRequest.type:type_name -> wsman.WorkspaceType 0, // 6: wsman.StopWorkspaceRequest.policy:type_name -> wsman.StopWorkspacePolicy - 32, // 7: wsman.DescribeWorkspaceResponse.status:type_name -> wsman.WorkspaceStatus + 34, // 7: wsman.DescribeWorkspaceResponse.status:type_name -> wsman.WorkspaceStatus 7, // 8: wsman.SubscribeRequest.must_match:type_name -> wsman.MetadataFilter - 32, // 9: wsman.SubscribeResponse.status:type_name -> wsman.WorkspaceStatus - 46, // 10: wsman.SubscribeResponse.header:type_name -> wsman.SubscribeResponse.HeaderEntry - 35, // 11: wsman.ControlPortRequest.spec:type_name -> wsman.PortSpec + 34, // 9: wsman.SubscribeResponse.status:type_name -> wsman.WorkspaceStatus + 49, // 10: wsman.SubscribeResponse.header:type_name -> wsman.SubscribeResponse.HeaderEntry + 37, // 11: wsman.ControlPortRequest.spec:type_name -> wsman.PortSpec 1, // 12: wsman.ControlAdmissionRequest.level:type_name -> wsman.AdmissionLevel - 38, // 13: wsman.WorkspaceStatus.metadata:type_name -> wsman.WorkspaceMetadata - 34, // 14: wsman.WorkspaceStatus.spec:type_name -> wsman.WorkspaceSpec + 40, // 13: wsman.WorkspaceStatus.metadata:type_name -> wsman.WorkspaceMetadata + 36, // 14: wsman.WorkspaceStatus.spec:type_name -> wsman.WorkspaceSpec 4, // 15: wsman.WorkspaceStatus.phase:type_name -> wsman.WorkspacePhase - 37, // 16: wsman.WorkspaceStatus.conditions:type_name -> wsman.WorkspaceConditions - 49, // 17: wsman.WorkspaceStatus.repo:type_name -> contentservice.GitStatus - 39, // 18: wsman.WorkspaceStatus.runtime:type_name -> wsman.WorkspaceRuntimeInfo - 40, // 19: wsman.WorkspaceStatus.auth:type_name -> wsman.WorkspaceAuthentication - 35, // 20: wsman.WorkspaceSpec.exposed_ports:type_name -> wsman.PortSpec + 39, // 16: wsman.WorkspaceStatus.conditions:type_name -> wsman.WorkspaceConditions + 52, // 17: wsman.WorkspaceStatus.repo:type_name -> contentservice.GitStatus + 41, // 18: wsman.WorkspaceStatus.runtime:type_name -> wsman.WorkspaceRuntimeInfo + 42, // 19: wsman.WorkspaceStatus.auth:type_name -> wsman.WorkspaceAuthentication + 37, // 20: wsman.WorkspaceSpec.exposed_ports:type_name -> wsman.PortSpec 6, // 21: wsman.WorkspaceSpec.type:type_name -> wsman.WorkspaceType - 33, // 22: wsman.WorkspaceSpec.ide_image:type_name -> wsman.IDEImage + 35, // 22: wsman.WorkspaceSpec.ide_image:type_name -> wsman.IDEImage 2, // 23: wsman.PortSpec.visibility:type_name -> wsman.PortVisibility 3, // 24: wsman.WorkspaceConditions.pulling_images:type_name -> wsman.WorkspaceConditionBool 3, // 25: wsman.WorkspaceConditions.final_backup_complete:type_name -> wsman.WorkspaceConditionBool 3, // 26: wsman.WorkspaceConditions.deployed:type_name -> wsman.WorkspaceConditionBool 3, // 27: wsman.WorkspaceConditions.network_not_ready:type_name -> wsman.WorkspaceConditionBool - 50, // 28: wsman.WorkspaceConditions.first_user_activity:type_name -> google.protobuf.Timestamp + 53, // 28: wsman.WorkspaceConditions.first_user_activity:type_name -> google.protobuf.Timestamp 3, // 29: wsman.WorkspaceConditions.stopped_by_request:type_name -> wsman.WorkspaceConditionBool - 36, // 30: wsman.WorkspaceConditions.volume_snapshot:type_name -> wsman.VolumeSnapshotInfo - 50, // 31: wsman.WorkspaceMetadata.started_at:type_name -> google.protobuf.Timestamp - 47, // 32: wsman.WorkspaceMetadata.annotations:type_name -> wsman.WorkspaceMetadata.AnnotationsEntry + 38, // 30: wsman.WorkspaceConditions.volume_snapshot:type_name -> wsman.VolumeSnapshotInfo + 53, // 31: wsman.WorkspaceMetadata.started_at:type_name -> google.protobuf.Timestamp + 50, // 32: wsman.WorkspaceMetadata.annotations:type_name -> wsman.WorkspaceMetadata.AnnotationsEntry 1, // 33: wsman.WorkspaceAuthentication.admission:type_name -> wsman.AdmissionLevel 5, // 34: wsman.StartWorkspaceSpec.feature_flags:type_name -> wsman.WorkspaceFeatureFlag - 51, // 35: wsman.StartWorkspaceSpec.initializer:type_name -> contentservice.WorkspaceInitializer - 35, // 36: wsman.StartWorkspaceSpec.ports:type_name -> wsman.PortSpec - 43, // 37: wsman.StartWorkspaceSpec.envvars:type_name -> wsman.EnvironmentVariable - 42, // 38: wsman.StartWorkspaceSpec.git:type_name -> wsman.GitSpec + 54, // 35: wsman.StartWorkspaceSpec.initializer:type_name -> contentservice.WorkspaceInitializer + 37, // 36: wsman.StartWorkspaceSpec.ports:type_name -> wsman.PortSpec + 45, // 37: wsman.StartWorkspaceSpec.envvars:type_name -> wsman.EnvironmentVariable + 44, // 38: wsman.StartWorkspaceSpec.git:type_name -> wsman.GitSpec 1, // 39: wsman.StartWorkspaceSpec.admission:type_name -> wsman.AdmissionLevel - 33, // 40: wsman.StartWorkspaceSpec.ide_image:type_name -> wsman.IDEImage - 36, // 41: wsman.StartWorkspaceSpec.volume_snapshot:type_name -> wsman.VolumeSnapshotInfo - 48, // 42: wsman.EnvironmentVariable.secret:type_name -> wsman.EnvironmentVariable.SecretKeyRef - 35, // 43: wsman.ExposedPorts.ports:type_name -> wsman.PortSpec + 35, // 40: wsman.StartWorkspaceSpec.ide_image:type_name -> wsman.IDEImage + 38, // 41: wsman.StartWorkspaceSpec.volume_snapshot:type_name -> wsman.VolumeSnapshotInfo + 51, // 42: wsman.EnvironmentVariable.secret:type_name -> wsman.EnvironmentVariable.SecretKeyRef + 37, // 43: wsman.ExposedPorts.ports:type_name -> wsman.PortSpec 8, // 44: wsman.WorkspaceManager.GetWorkspaces:input_type -> wsman.GetWorkspacesRequest 10, // 45: wsman.WorkspaceManager.StartWorkspace:input_type -> wsman.StartWorkspaceRequest 12, // 46: wsman.WorkspaceManager.StopWorkspace:input_type -> wsman.StopWorkspaceRequest @@ -3532,20 +3704,22 @@ var file_core_proto_depIdxs = []int32{ 24, // 53: wsman.WorkspaceManager.TakeSnapshot:input_type -> wsman.TakeSnapshotRequest 26, // 54: wsman.WorkspaceManager.ControlAdmission:input_type -> wsman.ControlAdmissionRequest 28, // 55: wsman.WorkspaceManager.DeleteVolumeSnapshot:input_type -> wsman.DeleteVolumeSnapshotRequest - 9, // 56: wsman.WorkspaceManager.GetWorkspaces:output_type -> wsman.GetWorkspacesResponse - 11, // 57: wsman.WorkspaceManager.StartWorkspace:output_type -> wsman.StartWorkspaceResponse - 13, // 58: wsman.WorkspaceManager.StopWorkspace:output_type -> wsman.StopWorkspaceResponse - 15, // 59: wsman.WorkspaceManager.DescribeWorkspace:output_type -> wsman.DescribeWorkspaceResponse - 31, // 60: wsman.WorkspaceManager.BackupWorkspace:output_type -> wsman.BackupWorkspaceResponse - 17, // 61: wsman.WorkspaceManager.Subscribe:output_type -> wsman.SubscribeResponse - 19, // 62: wsman.WorkspaceManager.MarkActive:output_type -> wsman.MarkActiveResponse - 21, // 63: wsman.WorkspaceManager.SetTimeout:output_type -> wsman.SetTimeoutResponse - 23, // 64: wsman.WorkspaceManager.ControlPort:output_type -> wsman.ControlPortResponse - 25, // 65: wsman.WorkspaceManager.TakeSnapshot:output_type -> wsman.TakeSnapshotResponse - 27, // 66: wsman.WorkspaceManager.ControlAdmission:output_type -> wsman.ControlAdmissionResponse - 29, // 67: wsman.WorkspaceManager.DeleteVolumeSnapshot:output_type -> wsman.DeleteVolumeSnapshotResponse - 56, // [56:68] is the sub-list for method output_type - 44, // [44:56] is the sub-list for method input_type + 32, // 56: wsman.WorkspaceManager.UpdateSSHKey:input_type -> wsman.UpdateSSHKeyRequest + 9, // 57: wsman.WorkspaceManager.GetWorkspaces:output_type -> wsman.GetWorkspacesResponse + 11, // 58: wsman.WorkspaceManager.StartWorkspace:output_type -> wsman.StartWorkspaceResponse + 13, // 59: wsman.WorkspaceManager.StopWorkspace:output_type -> wsman.StopWorkspaceResponse + 15, // 60: wsman.WorkspaceManager.DescribeWorkspace:output_type -> wsman.DescribeWorkspaceResponse + 31, // 61: wsman.WorkspaceManager.BackupWorkspace:output_type -> wsman.BackupWorkspaceResponse + 17, // 62: wsman.WorkspaceManager.Subscribe:output_type -> wsman.SubscribeResponse + 19, // 63: wsman.WorkspaceManager.MarkActive:output_type -> wsman.MarkActiveResponse + 21, // 64: wsman.WorkspaceManager.SetTimeout:output_type -> wsman.SetTimeoutResponse + 23, // 65: wsman.WorkspaceManager.ControlPort:output_type -> wsman.ControlPortResponse + 25, // 66: wsman.WorkspaceManager.TakeSnapshot:output_type -> wsman.TakeSnapshotResponse + 27, // 67: wsman.WorkspaceManager.ControlAdmission:output_type -> wsman.ControlAdmissionResponse + 29, // 68: wsman.WorkspaceManager.DeleteVolumeSnapshot:output_type -> wsman.DeleteVolumeSnapshotResponse + 33, // 69: wsman.WorkspaceManager.UpdateSSHKey:output_type -> wsman.UpdateSSHKeyResponse + 57, // [57:70] is the sub-list for method output_type + 44, // [44:57] is the sub-list for method input_type 44, // [44:44] is the sub-list for extension type_name 44, // [44:44] is the sub-list for extension extendee 0, // [0:44] is the sub-list for field type_name @@ -3858,7 +4032,7 @@ func file_core_proto_init() { } } file_core_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WorkspaceStatus); i { + switch v := v.(*UpdateSSHKeyRequest); i { case 0: return &v.state case 1: @@ -3870,7 +4044,7 @@ func file_core_proto_init() { } } file_core_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*IDEImage); i { + switch v := v.(*UpdateSSHKeyResponse); i { case 0: return &v.state case 1: @@ -3882,7 +4056,7 @@ func file_core_proto_init() { } } file_core_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WorkspaceSpec); i { + switch v := v.(*WorkspaceStatus); i { case 0: return &v.state case 1: @@ -3894,7 +4068,7 @@ func file_core_proto_init() { } } file_core_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PortSpec); i { + switch v := v.(*IDEImage); i { case 0: return &v.state case 1: @@ -3906,7 +4080,7 @@ func file_core_proto_init() { } } file_core_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeSnapshotInfo); i { + switch v := v.(*WorkspaceSpec); i { case 0: return &v.state case 1: @@ -3918,7 +4092,7 @@ func file_core_proto_init() { } } file_core_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WorkspaceConditions); i { + switch v := v.(*PortSpec); i { case 0: return &v.state case 1: @@ -3930,7 +4104,7 @@ func file_core_proto_init() { } } file_core_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WorkspaceMetadata); i { + switch v := v.(*VolumeSnapshotInfo); i { case 0: return &v.state case 1: @@ -3942,7 +4116,7 @@ func file_core_proto_init() { } } file_core_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WorkspaceRuntimeInfo); i { + switch v := v.(*WorkspaceConditions); i { case 0: return &v.state case 1: @@ -3954,7 +4128,7 @@ func file_core_proto_init() { } } file_core_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WorkspaceAuthentication); i { + switch v := v.(*WorkspaceMetadata); i { case 0: return &v.state case 1: @@ -3966,7 +4140,7 @@ func file_core_proto_init() { } } file_core_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StartWorkspaceSpec); i { + switch v := v.(*WorkspaceRuntimeInfo); i { case 0: return &v.state case 1: @@ -3978,7 +4152,7 @@ func file_core_proto_init() { } } file_core_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GitSpec); i { + switch v := v.(*WorkspaceAuthentication); i { case 0: return &v.state case 1: @@ -3990,7 +4164,7 @@ func file_core_proto_init() { } } file_core_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EnvironmentVariable); i { + switch v := v.(*StartWorkspaceSpec); i { case 0: return &v.state case 1: @@ -4002,6 +4176,30 @@ func file_core_proto_init() { } } file_core_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GitSpec); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_core_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EnvironmentVariable); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_core_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ExposedPorts); i { case 0: return &v.state @@ -4013,7 +4211,19 @@ func file_core_proto_init() { return nil } } - file_core_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + file_core_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SSHPublicKeys); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_core_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*EnvironmentVariable_SecretKeyRef); i { case 0: return &v.state @@ -4026,14 +4236,14 @@ func file_core_proto_init() { } } } - file_core_proto_msgTypes[31].OneofWrappers = []interface{}{} + file_core_proto_msgTypes[33].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_core_proto_rawDesc, NumEnums: 7, - NumMessages: 42, + NumMessages: 45, NumExtensions: 0, NumServices: 1, }, diff --git a/components/ws-manager-api/go/core_grpc.pb.go b/components/ws-manager-api/go/core_grpc.pb.go index d6223db655a3b5..77740f22b9406c 100644 --- a/components/ws-manager-api/go/core_grpc.pb.go +++ b/components/ws-manager-api/go/core_grpc.pb.go @@ -50,6 +50,8 @@ type WorkspaceManagerClient interface { ControlAdmission(ctx context.Context, in *ControlAdmissionRequest, opts ...grpc.CallOption) (*ControlAdmissionResponse, error) // deleteVolumeSnapshot asks ws-manager to delete specific volume snapshot and delete source from cloud provider as well DeleteVolumeSnapshot(ctx context.Context, in *DeleteVolumeSnapshotRequest, opts ...grpc.CallOption) (*DeleteVolumeSnapshotResponse, error) + // UpdateSSHKey update ssh keys + UpdateSSHKey(ctx context.Context, in *UpdateSSHKeyRequest, opts ...grpc.CallOption) (*UpdateSSHKeyResponse, error) } type workspaceManagerClient struct { @@ -191,6 +193,15 @@ func (c *workspaceManagerClient) DeleteVolumeSnapshot(ctx context.Context, in *D return out, nil } +func (c *workspaceManagerClient) UpdateSSHKey(ctx context.Context, in *UpdateSSHKeyRequest, opts ...grpc.CallOption) (*UpdateSSHKeyResponse, error) { + out := new(UpdateSSHKeyResponse) + err := c.cc.Invoke(ctx, "/wsman.WorkspaceManager/UpdateSSHKey", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // WorkspaceManagerServer is the server API for WorkspaceManager service. // All implementations must embed UnimplementedWorkspaceManagerServer // for forward compatibility @@ -219,6 +230,8 @@ type WorkspaceManagerServer interface { ControlAdmission(context.Context, *ControlAdmissionRequest) (*ControlAdmissionResponse, error) // deleteVolumeSnapshot asks ws-manager to delete specific volume snapshot and delete source from cloud provider as well DeleteVolumeSnapshot(context.Context, *DeleteVolumeSnapshotRequest) (*DeleteVolumeSnapshotResponse, error) + // UpdateSSHKey update ssh keys + UpdateSSHKey(context.Context, *UpdateSSHKeyRequest) (*UpdateSSHKeyResponse, error) mustEmbedUnimplementedWorkspaceManagerServer() } @@ -262,6 +275,9 @@ func (UnimplementedWorkspaceManagerServer) ControlAdmission(context.Context, *Co func (UnimplementedWorkspaceManagerServer) DeleteVolumeSnapshot(context.Context, *DeleteVolumeSnapshotRequest) (*DeleteVolumeSnapshotResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteVolumeSnapshot not implemented") } +func (UnimplementedWorkspaceManagerServer) UpdateSSHKey(context.Context, *UpdateSSHKeyRequest) (*UpdateSSHKeyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateSSHKey not implemented") +} func (UnimplementedWorkspaceManagerServer) mustEmbedUnimplementedWorkspaceManagerServer() {} // UnsafeWorkspaceManagerServer may be embedded to opt out of forward compatibility for this service. @@ -494,6 +510,24 @@ func _WorkspaceManager_DeleteVolumeSnapshot_Handler(srv interface{}, ctx context return interceptor(ctx, in, info, handler) } +func _WorkspaceManager_UpdateSSHKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateSSHKeyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorkspaceManagerServer).UpdateSSHKey(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/wsman.WorkspaceManager/UpdateSSHKey", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorkspaceManagerServer).UpdateSSHKey(ctx, req.(*UpdateSSHKeyRequest)) + } + return interceptor(ctx, in, info, handler) +} + // WorkspaceManager_ServiceDesc is the grpc.ServiceDesc for WorkspaceManager service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -545,6 +579,10 @@ var WorkspaceManager_ServiceDesc = grpc.ServiceDesc{ MethodName: "DeleteVolumeSnapshot", Handler: _WorkspaceManager_DeleteVolumeSnapshot_Handler, }, + { + MethodName: "UpdateSSHKey", + Handler: _WorkspaceManager_UpdateSSHKey_Handler, + }, }, Streams: []grpc.StreamDesc{ { diff --git a/components/ws-manager-api/go/mock/mock.go b/components/ws-manager-api/go/mock/mock.go index 13bd5308c50e97..49f03fdc77b6bc 100644 --- a/components/ws-manager-api/go/mock/mock.go +++ b/components/ws-manager-api/go/mock/mock.go @@ -339,6 +339,21 @@ func (mr *MockWorkspaceManagerServerMockRecorder) TakeSnapshot(arg0, arg1 interf return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TakeSnapshot", reflect.TypeOf((*MockWorkspaceManagerServer)(nil).TakeSnapshot), arg0, arg1) } +// UpdateSSHKey mocks base method. +func (m *MockWorkspaceManagerServer) UpdateSSHKey(arg0 context.Context, arg1 *api.UpdateSSHKeyRequest) (*api.UpdateSSHKeyResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateSSHKey", arg0, arg1) + ret0, _ := ret[0].(*api.UpdateSSHKeyResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateSSHKey indicates an expected call of UpdateSSHKey. +func (mr *MockWorkspaceManagerServerMockRecorder) UpdateSSHKey(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateSSHKey", reflect.TypeOf((*MockWorkspaceManagerServer)(nil).UpdateSSHKey), arg0, arg1) +} + // mustEmbedUnimplementedWorkspaceManagerServer mocks base method. func (m *MockWorkspaceManagerServer) mustEmbedUnimplementedWorkspaceManagerServer() { m.ctrl.T.Helper() @@ -736,3 +751,23 @@ func (mr *MockWorkspaceManagerClientMockRecorder) TakeSnapshot(arg0, arg1 interf varargs := append([]interface{}{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TakeSnapshot", reflect.TypeOf((*MockWorkspaceManagerClient)(nil).TakeSnapshot), varargs...) } + +// UpdateSSHKey mocks base method. +func (m *MockWorkspaceManagerClient) UpdateSSHKey(arg0 context.Context, arg1 *api.UpdateSSHKeyRequest, arg2 ...grpc.CallOption) (*api.UpdateSSHKeyResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "UpdateSSHKey", varargs...) + ret0, _ := ret[0].(*api.UpdateSSHKeyResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateSSHKey indicates an expected call of UpdateSSHKey. +func (mr *MockWorkspaceManagerClientMockRecorder) UpdateSSHKey(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateSSHKey", reflect.TypeOf((*MockWorkspaceManagerClient)(nil).UpdateSSHKey), varargs...) +} diff --git a/components/ws-manager-api/typescript/src/core_grpc_pb.d.ts b/components/ws-manager-api/typescript/src/core_grpc_pb.d.ts index c95b7e6dd2af4e..0c7ea9575175fd 100644 --- a/components/ws-manager-api/typescript/src/core_grpc_pb.d.ts +++ b/components/ws-manager-api/typescript/src/core_grpc_pb.d.ts @@ -28,6 +28,7 @@ interface IWorkspaceManagerService extends grpc.ServiceDefinition { @@ -138,6 +139,15 @@ interface IWorkspaceManagerService_IDeleteVolumeSnapshot extends grpc.MethodDefi responseSerialize: grpc.serialize; responseDeserialize: grpc.deserialize; } +interface IWorkspaceManagerService_IUpdateSSHKey extends grpc.MethodDefinition { + path: "/wsman.WorkspaceManager/UpdateSSHKey"; + requestStream: false; + responseStream: false; + requestSerialize: grpc.serialize; + requestDeserialize: grpc.deserialize; + responseSerialize: grpc.serialize; + responseDeserialize: grpc.deserialize; +} export const WorkspaceManagerService: IWorkspaceManagerService; @@ -154,6 +164,7 @@ export interface IWorkspaceManagerServer extends grpc.UntypedServiceImplementati takeSnapshot: grpc.handleUnaryCall; controlAdmission: grpc.handleUnaryCall; deleteVolumeSnapshot: grpc.handleUnaryCall; + updateSSHKey: grpc.handleUnaryCall; } export interface IWorkspaceManagerClient { @@ -192,6 +203,9 @@ export interface IWorkspaceManagerClient { deleteVolumeSnapshot(request: core_pb.DeleteVolumeSnapshotRequest, callback: (error: grpc.ServiceError | null, response: core_pb.DeleteVolumeSnapshotResponse) => void): grpc.ClientUnaryCall; deleteVolumeSnapshot(request: core_pb.DeleteVolumeSnapshotRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: core_pb.DeleteVolumeSnapshotResponse) => void): grpc.ClientUnaryCall; deleteVolumeSnapshot(request: core_pb.DeleteVolumeSnapshotRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: core_pb.DeleteVolumeSnapshotResponse) => void): grpc.ClientUnaryCall; + updateSSHKey(request: core_pb.UpdateSSHKeyRequest, callback: (error: grpc.ServiceError | null, response: core_pb.UpdateSSHKeyResponse) => void): grpc.ClientUnaryCall; + updateSSHKey(request: core_pb.UpdateSSHKeyRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: core_pb.UpdateSSHKeyResponse) => void): grpc.ClientUnaryCall; + updateSSHKey(request: core_pb.UpdateSSHKeyRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: core_pb.UpdateSSHKeyResponse) => void): grpc.ClientUnaryCall; } export class WorkspaceManagerClient extends grpc.Client implements IWorkspaceManagerClient { @@ -231,4 +245,7 @@ export class WorkspaceManagerClient extends grpc.Client implements IWorkspaceMan public deleteVolumeSnapshot(request: core_pb.DeleteVolumeSnapshotRequest, callback: (error: grpc.ServiceError | null, response: core_pb.DeleteVolumeSnapshotResponse) => void): grpc.ClientUnaryCall; public deleteVolumeSnapshot(request: core_pb.DeleteVolumeSnapshotRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: core_pb.DeleteVolumeSnapshotResponse) => void): grpc.ClientUnaryCall; public deleteVolumeSnapshot(request: core_pb.DeleteVolumeSnapshotRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: core_pb.DeleteVolumeSnapshotResponse) => void): grpc.ClientUnaryCall; + public updateSSHKey(request: core_pb.UpdateSSHKeyRequest, callback: (error: grpc.ServiceError | null, response: core_pb.UpdateSSHKeyResponse) => void): grpc.ClientUnaryCall; + public updateSSHKey(request: core_pb.UpdateSSHKeyRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: core_pb.UpdateSSHKeyResponse) => void): grpc.ClientUnaryCall; + public updateSSHKey(request: core_pb.UpdateSSHKeyRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: core_pb.UpdateSSHKeyResponse) => void): grpc.ClientUnaryCall; } diff --git a/components/ws-manager-api/typescript/src/core_grpc_pb.js b/components/ws-manager-api/typescript/src/core_grpc_pb.js index 2c150835f12d01..10de4976432844 100644 --- a/components/ws-manager-api/typescript/src/core_grpc_pb.js +++ b/components/ws-manager-api/typescript/src/core_grpc_pb.js @@ -276,6 +276,28 @@ function deserialize_wsman_TakeSnapshotResponse(buffer_arg) { return core_pb.TakeSnapshotResponse.deserializeBinary(new Uint8Array(buffer_arg)); } +function serialize_wsman_UpdateSSHKeyRequest(arg) { + if (!(arg instanceof core_pb.UpdateSSHKeyRequest)) { + throw new Error('Expected argument of type wsman.UpdateSSHKeyRequest'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_wsman_UpdateSSHKeyRequest(buffer_arg) { + return core_pb.UpdateSSHKeyRequest.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_wsman_UpdateSSHKeyResponse(arg) { + if (!(arg instanceof core_pb.UpdateSSHKeyResponse)) { + throw new Error('Expected argument of type wsman.UpdateSSHKeyResponse'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_wsman_UpdateSSHKeyResponse(buffer_arg) { + return core_pb.UpdateSSHKeyResponse.deserializeBinary(new Uint8Array(buffer_arg)); +} + var WorkspaceManagerService = exports.WorkspaceManagerService = { // getWorkspaces produces a list of running workspaces and their status @@ -422,6 +444,18 @@ deleteVolumeSnapshot: { responseSerialize: serialize_wsman_DeleteVolumeSnapshotResponse, responseDeserialize: deserialize_wsman_DeleteVolumeSnapshotResponse, }, + // UpdateSSHKey update ssh keys +updateSSHKey: { + path: '/wsman.WorkspaceManager/UpdateSSHKey', + requestStream: false, + responseStream: false, + requestType: core_pb.UpdateSSHKeyRequest, + responseType: core_pb.UpdateSSHKeyResponse, + requestSerialize: serialize_wsman_UpdateSSHKeyRequest, + requestDeserialize: deserialize_wsman_UpdateSSHKeyRequest, + responseSerialize: serialize_wsman_UpdateSSHKeyResponse, + responseDeserialize: deserialize_wsman_UpdateSSHKeyResponse, + }, }; exports.WorkspaceManagerClient = grpc.makeGenericClientConstructor(WorkspaceManagerService); diff --git a/components/ws-manager-api/typescript/src/core_pb.d.ts b/components/ws-manager-api/typescript/src/core_pb.d.ts index 39794549a873cf..342385e3a21b7f 100644 --- a/components/ws-manager-api/typescript/src/core_pb.d.ts +++ b/components/ws-manager-api/typescript/src/core_pb.d.ts @@ -580,6 +580,48 @@ export namespace BackupWorkspaceResponse { } } +export class UpdateSSHKeyRequest extends jspb.Message { + getId(): string; + setId(value: string): UpdateSSHKeyRequest; + clearKeysList(): void; + getKeysList(): Array; + setKeysList(value: Array): UpdateSSHKeyRequest; + addKeys(value: string, index?: number): string; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): UpdateSSHKeyRequest.AsObject; + static toObject(includeInstance: boolean, msg: UpdateSSHKeyRequest): UpdateSSHKeyRequest.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: UpdateSSHKeyRequest, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): UpdateSSHKeyRequest; + static deserializeBinaryFromReader(message: UpdateSSHKeyRequest, reader: jspb.BinaryReader): UpdateSSHKeyRequest; +} + +export namespace UpdateSSHKeyRequest { + export type AsObject = { + id: string, + keysList: Array, + } +} + +export class UpdateSSHKeyResponse extends jspb.Message { + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): UpdateSSHKeyResponse.AsObject; + static toObject(includeInstance: boolean, msg: UpdateSSHKeyResponse): UpdateSSHKeyResponse.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: UpdateSSHKeyResponse, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): UpdateSSHKeyResponse; + static deserializeBinaryFromReader(message: UpdateSSHKeyResponse, reader: jspb.BinaryReader): UpdateSSHKeyResponse; +} + +export namespace UpdateSSHKeyResponse { + export type AsObject = { + } +} + export class WorkspaceStatus extends jspb.Message { getId(): string; setId(value: string): WorkspaceStatus; @@ -965,6 +1007,10 @@ export class StartWorkspaceSpec extends jspb.Message { clearVolumeSnapshot(): void; getVolumeSnapshot(): VolumeSnapshotInfo | undefined; setVolumeSnapshot(value?: VolumeSnapshotInfo): StartWorkspaceSpec; + clearSshPublicKeysList(): void; + getSshPublicKeysList(): Array; + setSshPublicKeysList(value: Array): StartWorkspaceSpec; + addSshPublicKeys(value: string, index?: number): string; serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): StartWorkspaceSpec.AsObject; @@ -991,6 +1037,7 @@ export namespace StartWorkspaceSpec { ideImage?: IDEImage.AsObject, pb_class: string, volumeSnapshot?: VolumeSnapshotInfo.AsObject, + sshPublicKeysList: Array, } } @@ -1093,6 +1140,28 @@ export namespace ExposedPorts { } } +export class SSHPublicKeys extends jspb.Message { + clearKeysList(): void; + getKeysList(): Array; + setKeysList(value: Array): SSHPublicKeys; + addKeys(value: string, index?: number): string; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): SSHPublicKeys.AsObject; + static toObject(includeInstance: boolean, msg: SSHPublicKeys): SSHPublicKeys.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: SSHPublicKeys, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): SSHPublicKeys; + static deserializeBinaryFromReader(message: SSHPublicKeys, reader: jspb.BinaryReader): SSHPublicKeys; +} + +export namespace SSHPublicKeys { + export type AsObject = { + keysList: Array, + } +} + export enum StopWorkspacePolicy { NORMALLY = 0, IMMEDIATELY = 1, diff --git a/components/ws-manager-api/typescript/src/core_pb.js b/components/ws-manager-api/typescript/src/core_pb.js index a7d0ded57f8fa1..094f32df3e927a 100644 --- a/components/ws-manager-api/typescript/src/core_pb.js +++ b/components/ws-manager-api/typescript/src/core_pb.js @@ -48,6 +48,7 @@ goog.exportSymbol('proto.wsman.MarkActiveResponse', null, global); goog.exportSymbol('proto.wsman.MetadataFilter', null, global); goog.exportSymbol('proto.wsman.PortSpec', null, global); goog.exportSymbol('proto.wsman.PortVisibility', null, global); +goog.exportSymbol('proto.wsman.SSHPublicKeys', null, global); goog.exportSymbol('proto.wsman.SetTimeoutRequest', null, global); goog.exportSymbol('proto.wsman.SetTimeoutResponse', null, global); goog.exportSymbol('proto.wsman.StartWorkspaceRequest', null, global); @@ -60,6 +61,8 @@ goog.exportSymbol('proto.wsman.SubscribeRequest', null, global); goog.exportSymbol('proto.wsman.SubscribeResponse', null, global); goog.exportSymbol('proto.wsman.TakeSnapshotRequest', null, global); goog.exportSymbol('proto.wsman.TakeSnapshotResponse', null, global); +goog.exportSymbol('proto.wsman.UpdateSSHKeyRequest', null, global); +goog.exportSymbol('proto.wsman.UpdateSSHKeyResponse', null, global); goog.exportSymbol('proto.wsman.VolumeSnapshotInfo', null, global); goog.exportSymbol('proto.wsman.WorkspaceAuthentication', null, global); goog.exportSymbol('proto.wsman.WorkspaceConditionBool', null, global); @@ -596,6 +599,48 @@ if (goog.DEBUG && !COMPILED) { */ proto.wsman.BackupWorkspaceResponse.displayName = 'proto.wsman.BackupWorkspaceResponse'; } +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.wsman.UpdateSSHKeyRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.wsman.UpdateSSHKeyRequest.repeatedFields_, null); +}; +goog.inherits(proto.wsman.UpdateSSHKeyRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.wsman.UpdateSSHKeyRequest.displayName = 'proto.wsman.UpdateSSHKeyRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.wsman.UpdateSSHKeyResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.wsman.UpdateSSHKeyResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.wsman.UpdateSSHKeyResponse.displayName = 'proto.wsman.UpdateSSHKeyResponse'; +} /** * Generated by JsPbCodeGenerator. * @param {Array=} opt_data Optional initial data array, typically from a @@ -890,6 +935,27 @@ if (goog.DEBUG && !COMPILED) { */ proto.wsman.ExposedPorts.displayName = 'proto.wsman.ExposedPorts'; } +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.wsman.SSHPublicKeys = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.wsman.SSHPublicKeys.repeatedFields_, null); +}; +goog.inherits(proto.wsman.SSHPublicKeys, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.wsman.SSHPublicKeys.displayName = 'proto.wsman.SSHPublicKeys'; +} @@ -4719,6 +4785,293 @@ proto.wsman.BackupWorkspaceResponse.prototype.setUrl = function(value) { +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.wsman.UpdateSSHKeyRequest.repeatedFields_ = [2]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.wsman.UpdateSSHKeyRequest.prototype.toObject = function(opt_includeInstance) { + return proto.wsman.UpdateSSHKeyRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.wsman.UpdateSSHKeyRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.wsman.UpdateSSHKeyRequest.toObject = function(includeInstance, msg) { + var f, obj = { + id: jspb.Message.getFieldWithDefault(msg, 1, ""), + keysList: (f = jspb.Message.getRepeatedField(msg, 2)) == null ? undefined : f + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.wsman.UpdateSSHKeyRequest} + */ +proto.wsman.UpdateSSHKeyRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.wsman.UpdateSSHKeyRequest; + return proto.wsman.UpdateSSHKeyRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.wsman.UpdateSSHKeyRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.wsman.UpdateSSHKeyRequest} + */ +proto.wsman.UpdateSSHKeyRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setId(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.addKeys(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.wsman.UpdateSSHKeyRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.wsman.UpdateSSHKeyRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.wsman.UpdateSSHKeyRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.wsman.UpdateSSHKeyRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getId(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getKeysList(); + if (f.length > 0) { + writer.writeRepeatedString( + 2, + f + ); + } +}; + + +/** + * optional string id = 1; + * @return {string} + */ +proto.wsman.UpdateSSHKeyRequest.prototype.getId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.wsman.UpdateSSHKeyRequest} returns this + */ +proto.wsman.UpdateSSHKeyRequest.prototype.setId = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * repeated string keys = 2; + * @return {!Array} + */ +proto.wsman.UpdateSSHKeyRequest.prototype.getKeysList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 2)); +}; + + +/** + * @param {!Array} value + * @return {!proto.wsman.UpdateSSHKeyRequest} returns this + */ +proto.wsman.UpdateSSHKeyRequest.prototype.setKeysList = function(value) { + return jspb.Message.setField(this, 2, value || []); +}; + + +/** + * @param {string} value + * @param {number=} opt_index + * @return {!proto.wsman.UpdateSSHKeyRequest} returns this + */ +proto.wsman.UpdateSSHKeyRequest.prototype.addKeys = function(value, opt_index) { + return jspb.Message.addToRepeatedField(this, 2, value, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.wsman.UpdateSSHKeyRequest} returns this + */ +proto.wsman.UpdateSSHKeyRequest.prototype.clearKeysList = function() { + return this.setKeysList([]); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.wsman.UpdateSSHKeyResponse.prototype.toObject = function(opt_includeInstance) { + return proto.wsman.UpdateSSHKeyResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.wsman.UpdateSSHKeyResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.wsman.UpdateSSHKeyResponse.toObject = function(includeInstance, msg) { + var f, obj = { + + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.wsman.UpdateSSHKeyResponse} + */ +proto.wsman.UpdateSSHKeyResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.wsman.UpdateSSHKeyResponse; + return proto.wsman.UpdateSSHKeyResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.wsman.UpdateSSHKeyResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.wsman.UpdateSSHKeyResponse} + */ +proto.wsman.UpdateSSHKeyResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.wsman.UpdateSSHKeyResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.wsman.UpdateSSHKeyResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.wsman.UpdateSSHKeyResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.wsman.UpdateSSHKeyResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; +}; + + + if (jspb.Message.GENERATE_TO_OBJECT) { @@ -7373,7 +7726,7 @@ proto.wsman.WorkspaceAuthentication.prototype.setOwnerToken = function(value) { * @private {!Array} * @const */ -proto.wsman.StartWorkspaceSpec.repeatedFields_ = [3,5,6]; +proto.wsman.StartWorkspaceSpec.repeatedFields_ = [3,5,6,15]; @@ -7420,7 +7773,8 @@ proto.wsman.StartWorkspaceSpec.toObject = function(includeInstance, msg) { admission: jspb.Message.getFieldWithDefault(msg, 11, 0), ideImage: (f = msg.getIdeImage()) && proto.wsman.IDEImage.toObject(includeInstance, f), pb_class: jspb.Message.getFieldWithDefault(msg, 13, ""), - volumeSnapshot: (f = msg.getVolumeSnapshot()) && proto.wsman.VolumeSnapshotInfo.toObject(includeInstance, f) + volumeSnapshot: (f = msg.getVolumeSnapshot()) && proto.wsman.VolumeSnapshotInfo.toObject(includeInstance, f), + sshPublicKeysList: (f = jspb.Message.getRepeatedField(msg, 15)) == null ? undefined : f }; if (includeInstance) { @@ -7517,6 +7871,10 @@ proto.wsman.StartWorkspaceSpec.deserializeBinaryFromReader = function(msg, reade reader.readMessage(value,proto.wsman.VolumeSnapshotInfo.deserializeBinaryFromReader); msg.setVolumeSnapshot(value); break; + case 15: + var value = /** @type {string} */ (reader.readString()); + msg.addSshPublicKeys(value); + break; default: reader.skipField(); break; @@ -7643,6 +8001,13 @@ proto.wsman.StartWorkspaceSpec.serializeBinaryToWriter = function(message, write proto.wsman.VolumeSnapshotInfo.serializeBinaryToWriter ); } + f = message.getSshPublicKeysList(); + if (f.length > 0) { + writer.writeRepeatedString( + 15, + f + ); + } }; @@ -8015,6 +8380,43 @@ proto.wsman.StartWorkspaceSpec.prototype.hasVolumeSnapshot = function() { }; +/** + * repeated string ssh_public_keys = 15; + * @return {!Array} + */ +proto.wsman.StartWorkspaceSpec.prototype.getSshPublicKeysList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 15)); +}; + + +/** + * @param {!Array} value + * @return {!proto.wsman.StartWorkspaceSpec} returns this + */ +proto.wsman.StartWorkspaceSpec.prototype.setSshPublicKeysList = function(value) { + return jspb.Message.setField(this, 15, value || []); +}; + + +/** + * @param {string} value + * @param {number=} opt_index + * @return {!proto.wsman.StartWorkspaceSpec} returns this + */ +proto.wsman.StartWorkspaceSpec.prototype.addSshPublicKeys = function(value, opt_index) { + return jspb.Message.addToRepeatedField(this, 15, value, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.wsman.StartWorkspaceSpec} returns this + */ +proto.wsman.StartWorkspaceSpec.prototype.clearSshPublicKeysList = function() { + return this.setSshPublicKeysList([]); +}; + + @@ -8706,6 +9108,162 @@ proto.wsman.ExposedPorts.prototype.clearPortsList = function() { }; + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.wsman.SSHPublicKeys.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.wsman.SSHPublicKeys.prototype.toObject = function(opt_includeInstance) { + return proto.wsman.SSHPublicKeys.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.wsman.SSHPublicKeys} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.wsman.SSHPublicKeys.toObject = function(includeInstance, msg) { + var f, obj = { + keysList: (f = jspb.Message.getRepeatedField(msg, 1)) == null ? undefined : f + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.wsman.SSHPublicKeys} + */ +proto.wsman.SSHPublicKeys.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.wsman.SSHPublicKeys; + return proto.wsman.SSHPublicKeys.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.wsman.SSHPublicKeys} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.wsman.SSHPublicKeys} + */ +proto.wsman.SSHPublicKeys.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.addKeys(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.wsman.SSHPublicKeys.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.wsman.SSHPublicKeys.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.wsman.SSHPublicKeys} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.wsman.SSHPublicKeys.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getKeysList(); + if (f.length > 0) { + writer.writeRepeatedString( + 1, + f + ); + } +}; + + +/** + * repeated string keys = 1; + * @return {!Array} + */ +proto.wsman.SSHPublicKeys.prototype.getKeysList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 1)); +}; + + +/** + * @param {!Array} value + * @return {!proto.wsman.SSHPublicKeys} returns this + */ +proto.wsman.SSHPublicKeys.prototype.setKeysList = function(value) { + return jspb.Message.setField(this, 1, value || []); +}; + + +/** + * @param {string} value + * @param {number=} opt_index + * @return {!proto.wsman.SSHPublicKeys} returns this + */ +proto.wsman.SSHPublicKeys.prototype.addKeys = function(value, opt_index) { + return jspb.Message.addToRepeatedField(this, 1, value, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.wsman.SSHPublicKeys} returns this + */ +proto.wsman.SSHPublicKeys.prototype.clearKeysList = function() { + return this.setKeysList([]); +}; + + /** * @enum {number} */ diff --git a/components/ws-manager-api/typescript/src/promisified-client.ts b/components/ws-manager-api/typescript/src/promisified-client.ts index 2b00b3bb801582..2eef04b6bd82ed 100644 --- a/components/ws-manager-api/typescript/src/promisified-client.ts +++ b/components/ws-manager-api/typescript/src/promisified-client.ts @@ -28,6 +28,8 @@ import { DeleteVolumeSnapshotRequest, DeleteVolumeSnapshotResponse, TakeSnapshotResponse, + UpdateSSHKeyRequest, + UpdateSSHKeyResponse, } from "./core_pb"; import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing"; import * as opentracing from "opentracing"; @@ -372,6 +374,31 @@ export class PromisifiedWorkspaceManagerClient implements Disposable { }); } + public updateSSHPublicKey(ctx: TraceContext, request: UpdateSSHKeyRequest): Promise { + // we do not use the default options here as takeSnapshot can take a very long time - much longer than the default deadline allows + return this.retryIfUnavailable( + (attempt: number) => + new Promise((resolve, reject) => { + const span = TraceContext.startSpan(`/ws-manager/updateSSHPublicKey`, ctx); + span.log({ attempt }); + this.client.updateSSHKey( + request, + withTracing({ span }), + this.getDefaultUnaryOptions(), + (err, resp) => { + span.finish(); + if (err) { + TraceContext.setError(ctx, err); + reject(err); + } else { + resolve(resp); + } + }, + ); + }), + ); + } + protected getDefaultUnaryOptions(): Partial { let deadline = new Date(new Date().getTime() + 30000); return { diff --git a/components/ws-manager/pkg/manager/create.go b/components/ws-manager/pkg/manager/create.go index fe04eebf7d20f3..8ec972f23b5c49 100644 --- a/components/ws-manager/pkg/manager/create.go +++ b/components/ws-manager/pkg/manager/create.go @@ -381,6 +381,17 @@ func (m *Manager) createDefiniteWorkspacePod(startContext *startWorkspaceContext for k, v := range req.Metadata.Annotations { annotations[workspaceAnnotationPrefix+k] = v } + if len(startContext.Request.Spec.SshPublicKeys) != 0 { + spec := &api.SSHPublicKeys{ + Keys: startContext.Request.Spec.SshPublicKeys, + } + sshSpec, err := proto.Marshal(spec) + if err != nil { + return nil, xerrors.Errorf("cannot create remarshal of ssh key spec: %w", err) + } + sshSpecString := base64.StdEncoding.EncodeToString(sshSpec) + annotations[kubernetes.WorkspaceSSHPublicKeys] = sshSpecString + } // By default we embue our workspace pods with some tolerance towards pressure taints, // see https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/#taint-based-evictions diff --git a/components/ws-manager/pkg/manager/manager.go b/components/ws-manager/pkg/manager/manager.go index f4f8696c06c03f..c08e078b928a5c 100644 --- a/components/ws-manager/pkg/manager/manager.go +++ b/components/ws-manager/pkg/manager/manager.go @@ -6,6 +6,7 @@ package manager import ( "context" + "encoding/base64" "encoding/json" "fmt" "net/http" @@ -23,6 +24,7 @@ import ( "google.golang.org/grpc/peer" "google.golang.org/grpc/status" "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" corev1 "k8s.io/api/core/v1" k8serr "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -960,6 +962,46 @@ func (m *Manager) DescribeWorkspace(ctx context.Context, req *api.DescribeWorksp return result, nil } +// UpdateSSHKey update ssh keys +func (m *Manager) UpdateSSHKey(ctx context.Context, req *api.UpdateSSHKeyRequest) (res *api.UpdateSSHKeyResponse, err error) { + span, ctx := tracing.FromContext(ctx, "UpdateSSHKey") + tracing.ApplyOWI(span, log.OWI("", "", req.Id)) + defer tracing.FinishSpan(span, &err) + + err = retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) { + pod, err := m.findWorkspacePod(ctx, req.Id) + if err != nil { + return status.Errorf(codes.NotFound, "cannot find workspace: %v", err) + } + if pod == nil { + return status.Errorf(codes.NotFound, "workspace %s does not exist", req.Id) + } + tracing.ApplyOWI(span, wsk8s.GetOWIFromObject(&pod.ObjectMeta)) + + // update pod annotation + rspec, err := proto.Marshal(&api.SSHPublicKeys{ + Keys: req.Keys, + }) + if err != nil { + return status.Errorf(codes.InvalidArgument, "cannot serialise SSH keys: %v", err) + } + data := base64.StdEncoding.EncodeToString(rspec) + + if pod.Annotations[wsk8s.WorkspaceSSHPublicKeys] != data { + pod.Annotations[wsk8s.WorkspaceSSHPublicKeys] = data + // update pod + err = m.Clientset.Update(ctx, pod) + if err != nil { + // do not wrap error so we don't break the retry mechanism + return err + } + } + return nil + }) + + return &api.UpdateSSHKeyResponse{}, err +} + // Subscribe streams all status updates to a client func (m *Manager) Subscribe(req *api.SubscribeRequest, srv api.WorkspaceManager_SubscribeServer) (err error) { var sub subscriber = srv diff --git a/components/ws-manager/pkg/manager/testdata/cdwp_sshkeys.golden b/components/ws-manager/pkg/manager/testdata/cdwp_sshkeys.golden new file mode 100644 index 00000000000000..7bbbe7e6286de0 --- /dev/null +++ b/components/ws-manager/pkg/manager/testdata/cdwp_sshkeys.golden @@ -0,0 +1,266 @@ +{ + "reason": { + "metadata": { + "name": "ws-test", + "namespace": "default", + "creationTimestamp": null, + "labels": { + "app": "gitpod", + "component": "workspace", + "gitpod.io/networkpolicy": "default", + "gitpod.io/workspaceClass": "default", + "gpwsman": "true", + "headless": "false", + "metaID": "foobar", + "owner": "tester", + "project": "gitpod", + "team": "awesome", + "workspaceID": "test", + "workspaceType": "regular" + }, + "annotations": { + "cluster-autoscaler.kubernetes.io/safe-to-evict": "false", + "container.apparmor.security.beta.kubernetes.io/workspace": "unconfined", + "gitpod.io/attemptingToCreate": "true", + "gitpod.io/sshPublicKeys": "Cl9zc2gtZWQyNTUxOSBBQUFBQzNOemFDMWxaREkxTlRFNUFBQUFJSG0yK2tKSy93dWwxVkd3OXJmQ21kY0dqSjBXbUdiQmpDNXA1OTA4VUdnWiBnaXRwb2RAdW5rbm93bgqvAWVjZHNhLXNoYTItbmlzdHAyNTYgQUFBQUUyVmpaSE5oTFhOb1lUSXRibWx6ZEhBeU5UWUFBQUFJYm1semRIQXlOVFlBQUFCQkJNL0Eva3JhV2pXeTI4blZmc1Y0R2EyRjROQUFWT0dwL1YzazNVOThuT3QyVVNZd3U3RXRTcUVPN1BDUFErK3pOSmR1aFk1SjBmYm42TzA0K25LdmR0OD0gZ2l0cG9kQHVua25vd24=", + "gitpod/admission": "admit_owner_only", + "gitpod/contentInitializer": "GmcKZXdvcmtzcGFjZXMvY3J5cHRpYy1pZC1nb2VzLWhlcmcvZmQ2MjgwNGItNGNhYi0xMWU5LTg0M2EtNGU2NDUzNzMwNDhlLnRhckBnaXRwb2QtZGV2LXVzZXItY2hyaXN0ZXN0aW5n", + "gitpod/id": "test", + "gitpod/imageSpec": "CrwBZXUuZ2NyLmlvL2dpdHBvZC1kZXYvd29ya3NwYWNlLWltYWdlcy9hYzFjMDc1NTAwNzk2NmU0ZDZlMDkwZWE4MjE3MjlhYzc0N2QyMmFjL2V1Lmdjci5pby9naXRwb2QtZGV2L3dvcmtzcGFjZS1iYXNlLWltYWdlcy9naXRodWIuY29tL3R5cGVmb3gvZ2l0cG9kOjgwYTdkNDI3YTFmY2QzNDZkNDIwNjAzZDgwYTMxZDU3Y2Y3NWE3YWYSNGV1Lmdjci5pby9naXRwb2QtY29yZS1kZXYvYnVpZC90aGVpYS1pZGU6c29tZXZlcnNpb24=", + "gitpod/never-ready": "true", + "gitpod/ownerToken": "%7J'[Of/8NDiWE+9F,I6^Jcj_1\u0026}-F8p", + "gitpod/servicePrefix": "foobarservice", + "gitpod/url": "test-foobarservice-gitpod.io", + "seccomp.security.alpha.kubernetes.io/pod": "localhost/workspace-default" + }, + "finalizers": [ + "gitpod.io/finalizer" + ] + }, + "spec": { + "volumes": [ + { + "name": "vol-this-workspace", + "hostPath": { + "path": "/tmp/workspaces/test", + "type": "DirectoryOrCreate" + } + }, + { + "name": "daemon-mount", + "hostPath": { + "path": "/tmp/workspaces/test-daemon", + "type": "DirectoryOrCreate" + } + } + ], + "containers": [ + { + "name": "workspace", + "image": "registry-facade:8080/remote/test", + "command": [ + "/.supervisor/workspacekit", + "ring0" + ], + "ports": [ + { + "containerPort": 23000 + }, + { + "name": "supervisor", + "containerPort": 22999 + } + ], + "env": [ + { + "name": "GITPOD_REPO_ROOT", + "value": "/workspace" + }, + { + "name": "GITPOD_REPO_ROOTS", + "value": "/workspace" + }, + { + "name": "GITPOD_CLI_APITOKEN", + "value": "Ab=5=rRA*9:C'T{;RRB\u003e]vK2p6`fFfrS" + }, + { + "name": "GITPOD_OWNER_ID", + "value": "tester" + }, + { + "name": "GITPOD_WORKSPACE_ID", + "value": "foobar" + }, + { + "name": "GITPOD_INSTANCE_ID", + "value": "test" + }, + { + "name": "GITPOD_THEIA_PORT", + "value": "23000" + }, + { + "name": "THEIA_WORKSPACE_ROOT", + "value": "/workspace" + }, + { + "name": "GITPOD_HOST", + "value": "gitpod.io" + }, + { + "name": "GITPOD_WORKSPACE_URL", + "value": "test-foobarservice-gitpod.io" + }, + { + "name": "THEIA_SUPERVISOR_ENDPOINT", + "value": ":22999" + }, + { + "name": "THEIA_WEBVIEW_EXTERNAL_ENDPOINT", + "value": "webview-{{hostname}}" + }, + { + "name": "THEIA_MINI_BROWSER_HOST_PATTERN", + "value": "browser-{{hostname}}" + }, + { + "name": "GITPOD_GIT_USER_NAME", + "value": "usernameGoesHere" + }, + { + "name": "GITPOD_GIT_USER_EMAIL", + "value": "some@user.com" + }, + { + "name": "foo", + "value": "bar" + }, + { + "name": "GITPOD_INTERVAL", + "value": "30000" + }, + { + "name": "GITPOD_MEMORY", + "value": "999" + } + ], + "resources": { + "limits": { + "cpu": "900m", + "memory": "1G" + }, + "requests": { + "cpu": "899m", + "ephemeral-storage": "5Gi", + "memory": "999M" + } + }, + "volumeMounts": [ + { + "name": "vol-this-workspace", + "mountPath": "/workspace", + "mountPropagation": "HostToContainer" + }, + { + "name": "daemon-mount", + "mountPath": "/.workspace", + "mountPropagation": "HostToContainer" + } + ], + "readinessProbe": { + "httpGet": { + "path": "/_supervisor/v1/status/content/wait/true", + "port": 22999, + "scheme": "HTTP" + }, + "initialDelaySeconds": 2, + "timeoutSeconds": 1, + "periodSeconds": 1, + "successThreshold": 1, + "failureThreshold": 600 + }, + "terminationMessagePolicy": "File", + "imagePullPolicy": "IfNotPresent", + "securityContext": { + "capabilities": { + "add": [ + "AUDIT_WRITE", + "FSETID", + "KILL", + "NET_BIND_SERVICE", + "SYS_PTRACE" + ], + "drop": [ + "SETPCAP", + "CHOWN", + "NET_RAW", + "DAC_OVERRIDE", + "FOWNER", + "SYS_CHROOT", + "SETFCAP", + "SETUID", + "SETGID" + ] + }, + "privileged": false, + "runAsUser": 33333, + "runAsGroup": 33333, + "runAsNonRoot": true, + "readOnlyRootFilesystem": false, + "allowPrivilegeEscalation": true + } + } + ], + "restartPolicy": "Never", + "serviceAccountName": "workspace", + "automountServiceAccountToken": false, + "securityContext": {}, + "hostname": "foobar", + "affinity": { + "nodeAffinity": { + "requiredDuringSchedulingIgnoredDuringExecution": { + "nodeSelectorTerms": [ + { + "matchExpressions": [ + { + "key": "gitpod.io/workload_workspace_regular", + "operator": "Exists" + }, + { + "key": "gitpod.io/ws-daemon_ready_ns_default", + "operator": "Exists" + }, + { + "key": "gitpod.io/registry-facade_ready_ns_default", + "operator": "Exists" + } + ] + } + ] + } + } + }, + "tolerations": [ + { + "key": "node.kubernetes.io/disk-pressure", + "operator": "Exists", + "effect": "NoExecute" + }, + { + "key": "node.kubernetes.io/memory-pressure", + "operator": "Exists", + "effect": "NoExecute" + }, + { + "key": "node.kubernetes.io/network-unavailable", + "operator": "Exists", + "effect": "NoExecute", + "tolerationSeconds": 30 + } + ], + "enableServiceLinks": false + }, + "status": {} + } +} diff --git a/components/ws-manager/pkg/manager/testdata/cdwp_sshkeys.json b/components/ws-manager/pkg/manager/testdata/cdwp_sshkeys.json new file mode 100644 index 00000000000000..65e669b18ab1da --- /dev/null +++ b/components/ws-manager/pkg/manager/testdata/cdwp_sshkeys.json @@ -0,0 +1,78 @@ +{ + "defaultTemplate": { + "spec": { + "dnsConfig": { + "nameservers": [ + "1.1.1.1", + "8.8.8.8" + ] + }, + "dnsPolicy": "None", + "imagePullSecrets": [ + { + "name": "dockerhub-typefox" + }, + { + "name": "eu.gcr.io-gitpod" + } + ], + "affinity": { + "nodeAffinity": { + "requiredDuringSchedulingIgnoredDuringExecution": { + "nodeSelectorTerms": [ + { + "matchExpressions": [ + { + "key": "gitpod.io/workload_workspace_regular", + "operator": "In", + "values": [ + "true" + ] + } + ] + } + ] + } + } + }, + "containers": [ + { + "name": "workspace", + "env": [ + { "name": "some-envvar", "value": "foofoo" } + ] + } + ] + } + }, + "spec": { + "ideImage": { + "webRef": "eu.gcr.io/gitpod-core-dev/buid/theia-ide:someversion" + }, + "workspaceImage": "eu.gcr.io/gitpod-dev/workspace-images/ac1c0755007966e4d6e090ea821729ac747d22ac/eu.gcr.io/gitpod-dev/workspace-base-images/github.com/typefox/gitpod:80a7d427a1fcd346d420603d80a31d57cf75a7af", + "initializer": { + "snapshot": { + "snapshot": "workspaces/cryptic-id-goes-herg/fd62804b-4cab-11e9-843a-4e645373048e.tar@gitpod-dev-user-christesting" + } + }, + "ports": [ + { + "port": 8080 + } + ], + "envvars": [ + { + "name": "foo", + "value": "bar" + } + ], + "git": { + "username": "usernameGoesHere", + "email": "some@user.com" + }, + "ssh_public_keys": [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHm2+kJK/wul1VGw9rfCmdcGjJ0WmGbBjC5p5908UGgZ gitpod@unknown", + "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBM/A/kraWjWy28nVfsV4Ga2F4NAAVOGp/V3k3U98nOt2USYwu7EtSqEO7PCPQ++zNJduhY5J0fbn6O04+nKvdt8= gitpod@unknown" + ] + } +} diff --git a/components/ws-proxy/pkg/proxy/infoprovider.go b/components/ws-proxy/pkg/proxy/infoprovider.go index c393722b55c4aa..335a64e3e03891 100644 --- a/components/ws-proxy/pkg/proxy/infoprovider.go +++ b/components/ws-proxy/pkg/proxy/infoprovider.go @@ -6,11 +6,13 @@ package proxy import ( "context" + "encoding/base64" "net/url" "strings" "time" "golang.org/x/xerrors" + "google.golang.org/protobuf/proto" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -62,7 +64,8 @@ type WorkspaceInfo struct { Auth *wsapi.WorkspaceAuthentication StartedAt time.Time - OwnerUserId string + OwnerUserId string + SSHPublicKeys []string } // RemoteWorkspaceInfoProvider provides (cached) infos about running workspaces that it queries from ws-manager. @@ -162,6 +165,7 @@ func mapPodToWorkspaceInfo(pod *corev1.Pod) *WorkspaceInfo { Auth: &wsapi.WorkspaceAuthentication{Admission: admission, OwnerToken: ownerToken}, StartedAt: pod.CreationTimestamp.Time, OwnerUserId: pod.Labels[kubernetes.OwnerLabel], + SSHPublicKeys: extractUserSSHPublicKeys(pod), } } @@ -218,3 +222,19 @@ func extractExposedPorts(pod *corev1.Pod) *api.ExposedPorts { return &api.ExposedPorts{} } + +func extractUserSSHPublicKeys(pod *corev1.Pod) []string { + if data, ok := pod.Annotations[kubernetes.WorkspaceSSHPublicKeys]; ok && len(data) != 0 { + specPB, err := base64.StdEncoding.DecodeString(data) + if err != nil { + return nil + } + var spec api.SSHPublicKeys + err = proto.Unmarshal(specPB, &spec) + if err != nil { + return nil + } + return spec.Keys + } + return nil +} diff --git a/components/ws-proxy/pkg/sshproxy/server.go b/components/ws-proxy/pkg/sshproxy/server.go index de1220b917654b..4cf17cf2de5505 100644 --- a/components/ws-proxy/pkg/sshproxy/server.go +++ b/components/ws-proxy/pkg/sshproxy/server.go @@ -6,6 +6,7 @@ package sshproxy import ( "context" + "crypto/subtle" "net" "regexp" "strings" @@ -107,24 +108,27 @@ func New(signers []ssh.Signer, workspaceInfoProvider p.WorkspaceInfoProvider, he ServerVersion: "SSH-2.0-GITPOD-GATEWAY", PasswordCallback: func(conn ssh.ConnMetadata, password []byte) (perm *ssh.Permissions, err error) { workspaceId, ownerToken := conn.User(), string(password) - wsInfo, err := server.Authenticator(workspaceId, ownerToken) + wsInfo, err := server.GetWorkspaceInfo(workspaceId) defer func() { server.TrackSSHConnection(wsInfo, "auth", err) }() if err != nil { - if err == ErrAuthFailed { - return - } args := strings.Split(conn.User(), "#") if len(args) != 2 { return } workspaceId, ownerToken = args[0], args[1] - wsInfo, err = server.Authenticator(workspaceId, ownerToken) - if err == nil { - err = ErrMissPrivateKey + wsInfo, err = server.GetWorkspaceInfo(workspaceId) + if err != nil { + return nil, ErrWorkspaceNotFound + } + if wsInfo.Auth.OwnerToken == ownerToken { + return nil, ErrMissPrivateKey } - return + return nil, ErrAuthFailed + } + if wsInfo.Auth.OwnerToken != ownerToken { + return nil, ErrAuthFailed } return &ssh.Permissions{ Extensions: map[string]string{ @@ -132,19 +136,29 @@ func New(signers []ssh.Signer, workspaceInfoProvider p.WorkspaceInfoProvider, he }, }, nil }, - PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { + PublicKeyCallback: func(conn ssh.ConnMetadata, pk ssh.PublicKey) (perm *ssh.Permissions, err error) { args := strings.Split(conn.User(), "#") - // workspaceId#ownerToken - if len(args) != 2 { - return nil, ErrUsernameFormat + workspaceId := args[0] + wsInfo, err := server.GetWorkspaceInfo(workspaceId) + if err != nil { + return nil, err } - workspaceId, ownerToken := args[0], args[1] - wsInfo, err := server.Authenticator(workspaceId, ownerToken) defer func() { server.TrackSSHConnection(wsInfo, "auth", err) }() - if err != nil { - return nil, err + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + ok, _ := server.VerifyPublicKey(ctx, wsInfo, pk) + if ok { + return &ssh.Permissions{ + Extensions: map[string]string{ + "workspaceId": workspaceId, + }, + }, nil + } + // workspaceId#ownerToken + if len(args) != 2 || wsInfo.Auth.OwnerToken != args[1] { + return nil, ErrAuthFailed } return &ssh.Permissions{ Extensions: map[string]string{ @@ -292,7 +306,7 @@ func (s *Server) HandleConn(c net.Conn) { cancel() } -func (s *Server) Authenticator(workspaceId, ownerToken string) (*p.WorkspaceInfo, error) { +func (s *Server) GetWorkspaceInfo(workspaceId string) (*p.WorkspaceInfo, error) { wsInfo := s.workspaceInfoProvider.WorkspaceInfo(workspaceId) if wsInfo == nil { if matched, _ := regexp.Match(workspaceIDRegex, []byte(workspaceId)); matched { @@ -300,9 +314,6 @@ func (s *Server) Authenticator(workspaceId, ownerToken string) (*p.WorkspaceInfo } return nil, ErrWorkspaceIDInvalid } - if wsInfo.Auth.OwnerToken != ownerToken { - return wsInfo, ErrAuthFailed - } return wsInfo, nil } @@ -329,6 +340,21 @@ func (s *Server) TrackSSHConnection(wsInfo *p.WorkspaceInfo, phase string, err e }) } +func (s *Server) VerifyPublicKey(ctx context.Context, wsInfo *p.WorkspaceInfo, pk ssh.PublicKey) (bool, error) { + for _, keyStr := range wsInfo.SSHPublicKeys { + key, _, _, _, err := ssh.ParseAuthorizedKey([]byte(keyStr)) + if err != nil { + continue + } + keyData := key.Marshal() + pkd := pk.Marshal() + if len(keyData) == len(pkd) && subtle.ConstantTimeCompare(keyData, pkd) == 1 { + return true, nil + } + } + return false, nil +} + func (s *Server) GetWorkspaceSSHKey(ctx context.Context, workspaceIP string) (ssh.Signer, error) { supervisorConn, err := grpc.Dial(workspaceIP+":22999", grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil {