Skip to content

Commit 9dec31c

Browse files
committed
[ws-manager-mk2] Support public SSH keys
1 parent 00229da commit 9dec31c

File tree

5 files changed

+75
-1
lines changed

5 files changed

+75
-1
lines changed

components/ws-manager-api/go/crd/v1/workspace_types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ type WorkspaceSpec struct {
4545

4646
// +kubebuilder:validation:MinItems=0
4747
Ports []PortSpec `json:"ports"`
48+
49+
SshPublicKeys string `json:"sshPublicKey,omitempty"`
4850
}
4951

5052
type Ownership struct {

components/ws-manager-mk2/config/crd/bases/workspace.gitpod.io_workspaces.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ spec:
151151
type: object
152152
minItems: 0
153153
type: array
154+
sshPublicKey:
155+
type: string
154156
sysEnvVars:
155157
items:
156158
description: EnvVar represents an environment variable present in

components/ws-manager-mk2/controllers/create.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"k8s.io/utils/pointer"
2525
ctrl "sigs.k8s.io/controller-runtime"
2626

27+
"github.com/gitpod-io/gitpod/common-go/kubernetes"
2728
wsk8s "github.com/gitpod-io/gitpod/common-go/kubernetes"
2829
"github.com/gitpod-io/gitpod/common-go/tracing"
2930
csapi "github.com/gitpod-io/gitpod/content-service/api"
@@ -295,6 +296,10 @@ func createDefiniteWorkspacePod(sctx *startWorkspaceContext) (*corev1.Pod, error
295296
annotations[k] = v
296297
}
297298

299+
if sctx.Workspace.Spec.SshPublicKeys != "" {
300+
annotations[kubernetes.WorkspaceSSHPublicKeys] = sctx.Workspace.Spec.SshPublicKeys
301+
}
302+
298303
// By default we embue our workspace pods with some tolerance towards pressure taints,
299304
// see https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/#taint-based-evictions
300305
// for more details. As hope/assume that the pressure might go away in this time.

components/ws-manager-mk2/controllers/workspace_controller.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1919
"sigs.k8s.io/controller-runtime/pkg/log"
2020

21+
"github.com/gitpod-io/gitpod/common-go/kubernetes"
2122
wsk8s "github.com/gitpod-io/gitpod/common-go/kubernetes"
2223
config "github.com/gitpod-io/gitpod/ws-manager/api/config"
2324
workspacev1 "github.com/gitpod-io/gitpod/ws-manager/api/crd/v1"
@@ -241,6 +242,13 @@ func (r *WorkspaceReconciler) actOnStatus(ctx context.Context, workspace *worksp
241242
return ctrl.Result{Requeue: true}, err
242243
}
243244

245+
case workspace.Spec.SshPublicKeys != pod.Annotations[kubernetes.WorkspaceSSHPublicKeys]:
246+
pod.Annotations[kubernetes.WorkspaceSSHPublicKeys] = workspace.Spec.SshPublicKeys
247+
err := r.Client.Update(ctx, pod)
248+
if err != nil {
249+
return ctrl.Result{Requeue: true}, err
250+
}
251+
244252
// we've disposed already - try to remove the finalizer and call it a day
245253
case workspace.Status.Phase == workspacev1.WorkspacePhaseStopped:
246254
hadFinalizer := controllerutil.ContainsFinalizer(pod, workspacev1.GitpodFinalizerName)

components/ws-manager-mk2/service/manager.go

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package service
66

77
import (
88
"context"
9+
"encoding/base64"
910
"fmt"
1011
"strconv"
1112
"sync"
@@ -188,6 +189,11 @@ func (wsm *WorkspaceManagerServer) StartWorkspace(ctx context.Context, req *wsma
188189
}
189190
}
190191

192+
sshPublicKeys, err := setSshPublicKeys(req.Spec.SshPublicKeys)
193+
if err != nil {
194+
return nil, status.Errorf(codes.InvalidArgument, "invalid ssh public keys")
195+
}
196+
191197
ws := workspacev1.Workspace{
192198
TypeMeta: metav1.TypeMeta{
193199
APIVersion: workspacev1.GroupVersion.String(),
@@ -230,7 +236,8 @@ func (wsm *WorkspaceManagerServer) StartWorkspace(ctx context.Context, req *wsma
230236
Admission: workspacev1.AdmissionSpec{
231237
Level: admissionLevel,
232238
},
233-
Ports: ports,
239+
Ports: ports,
240+
SshPublicKeys: sshPublicKeys,
234241
},
235242
}
236243
controllerutil.AddFinalizer(&ws, workspacev1.GitpodFinalizerName)
@@ -523,6 +530,28 @@ func (wsm *WorkspaceManagerServer) ControlAdmission(ctx context.Context, req *ws
523530
return &wsmanapi.ControlAdmissionResponse{}, nil
524531
}
525532

533+
func (wsm *WorkspaceManagerServer) UpdateSSHKey(ctx context.Context, req *api.UpdateSSHKeyRequest) (res *api.UpdateSSHKeyResponse, err error) {
534+
span, ctx := tracing.FromContext(ctx, "UpdateSSHKey")
535+
tracing.ApplyOWI(span, log.OWI("", "", req.Id))
536+
defer tracing.FinishSpan(span, &err)
537+
538+
if err = validateUpdateSSHKeyRequest(req); err != nil {
539+
return &api.UpdateSSHKeyResponse{}, err
540+
}
541+
542+
err = wsm.modifyWorkspace(ctx, req.Id, false, func(ws *workspacev1.Workspace) error {
543+
sshKeys, err := setSshPublicKeys(req.Keys)
544+
if err != nil {
545+
return err
546+
}
547+
548+
ws.Spec.SshPublicKeys = sshKeys
549+
return nil
550+
})
551+
552+
return &api.UpdateSSHKeyResponse{}, err
553+
}
554+
526555
// modifyWorkspace modifies a workspace object using the mod function. If the mod function returns a gRPC status error, that error
527556
// is returned directly. If mod returns a non-gRPC error it is turned into one.
528557
func (wsm *WorkspaceManagerServer) modifyWorkspace(ctx context.Context, id string, updateStatus bool, mod func(ws *workspacev1.Workspace) error) error {
@@ -586,6 +615,19 @@ func validateStartWorkspaceRequest(req *api.StartWorkspaceRequest) error {
586615
return nil
587616
}
588617

618+
func validateUpdateSSHKeyRequest(req *api.UpdateSSHKeyRequest) error {
619+
err := validation.ValidateStruct(req,
620+
validation.Field(&req.Id, validation.Required),
621+
validation.Field(&req.Keys, validation.Required),
622+
)
623+
624+
if err != nil {
625+
return status.Errorf(codes.InvalidArgument, "invalid request: %v", err)
626+
}
627+
628+
return nil
629+
}
630+
589631
func isValidWorkspaceType(value interface{}) error {
590632
s, ok := value.(api.WorkspaceType)
591633
if !ok {
@@ -653,6 +695,21 @@ func setEnvironment(envs []*wsmanapi.EnvironmentVariable) []corev1.EnvVar {
653695
return envVars
654696
}
655697

698+
func setSshPublicKeys(keys []string) (string, error) {
699+
if len(keys) != 0 {
700+
spec := &api.SSHPublicKeys{
701+
Keys: keys,
702+
}
703+
sshSpec, err := proto.Marshal(spec)
704+
if err != nil {
705+
return "", xerrors.Errorf("cannot create remarshal of ssh key spec: %w", err)
706+
}
707+
return base64.StdEncoding.EncodeToString(sshSpec), nil
708+
}
709+
710+
return "", nil
711+
}
712+
656713
func extractWorkspaceStatus(ws *workspacev1.Workspace) *wsmanapi.WorkspaceStatus {
657714
version, _ := strconv.ParseUint(ws.ResourceVersion, 10, 64)
658715

0 commit comments

Comments
 (0)