Skip to content

Commit 060371f

Browse files
committed
fix(ws): add secrets attribute to backend API schema
related: #239 This commit brings partial support for secrets to the backend API. It enables the `frontend` component to successfully create a Workspace through the "wizard flow". **HOWEVER**, it is important to note this secrets attribute is not supported within the `controller` component yet - as #240 is not yet merged. To unblock the `frontend` - the logic contained in this commit simply adds the necessary scaffolding to accept the `secrets` attribute defined within `volumes`. Once umarshalled, the backend essentially ignores this data. Code to fully enable the end to end flow is included in this PR - but simply commented out with `TODO:` comments denoting what can be uncommented once #240 is merged into `notebooks-v2`. A test is also presently disabled with `XIt` - and can also be enabled when required code present. Changes were initially coded against the branch provided on #240 to verify full end-to-end behavior. Once confirmed, commit was rebased onto `notebooks-v2`, relevant code commented out as described above, and behavior retested to ensure desired outcome. In summary, with these changes: - `backend` API accepts `volumes.secrets` in the _Create_ payload - secrets data is **NOT USED** when programmatically constructing the Workspace CR - Resultant workspace has no `secrets` data - irrespective of it if was provided in the payload or not. Signed-off-by: Andy Stoneberg <[email protected]>
1 parent 45d778a commit 060371f

File tree

7 files changed

+216
-4
lines changed

7 files changed

+216
-4
lines changed

workspaces/backend/api/workspaces_handler_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,9 @@ var _ = Describe("Workspaces Handler", func() {
770770
}
771771
Expect(createdWorkspace.Spec.PodTemplate.Volumes.Data).To(Equal(expected))
772772

773+
// TODO: Verify secrets once #240 merged
774+
// Expect(createdWorkspace.Spec.PodTemplate.Volumes.Secrets).To(BeEmpty())
775+
773776
By("creating an HTTP request to delete the Workspace")
774777
path = strings.Replace(WorkspacesByNamePath, ":"+NamespacePathParam, namespaceNameCrud, 1)
775778
path = strings.Replace(path, ":"+ResourceNamePathParam, workspaceName, 1)
@@ -805,6 +808,70 @@ var _ = Describe("Workspaces Handler", func() {
805808
Expect(apierrors.IsNotFound(err)).To(BeTrue())
806809
})
807810

811+
// TODO: Change to It when #240 merged
812+
XIt("should create a workspace with secrets", func() {
813+
// Create a workspace with secrets
814+
workspace := &models.WorkspaceCreate{
815+
Name: "test-workspace",
816+
Kind: "test-kind",
817+
PodTemplate: models.PodTemplateMutate{
818+
Options: models.PodTemplateOptionsMutate{
819+
ImageConfig: "test-image",
820+
PodConfig: "test-config",
821+
},
822+
Volumes: models.PodVolumesMutate{
823+
Data: []models.PodVolumeMount{
824+
{
825+
PVCName: "test-pvc",
826+
MountPath: "/data",
827+
},
828+
},
829+
Secrets: []models.PodSecretMount{
830+
{
831+
SecretName: "test-secret",
832+
MountPath: "/secrets",
833+
DefaultMode: int32(0o644),
834+
},
835+
},
836+
},
837+
},
838+
}
839+
840+
// Create the workspace using the API handler
841+
bodyEnvelope := WorkspaceCreateEnvelope{Data: workspace}
842+
bodyEnvelopeJSON, err := json.Marshal(bodyEnvelope)
843+
Expect(err).NotTo(HaveOccurred())
844+
845+
path := strings.Replace(WorkspacesByNamespacePath, ":"+NamespacePathParam, namespaceNameCrud, 1)
846+
req, err := http.NewRequest(http.MethodPost, path, strings.NewReader(string(bodyEnvelopeJSON)))
847+
Expect(err).NotTo(HaveOccurred())
848+
req.Header.Set("Content-Type", "application/json")
849+
req.Header.Set(userIdHeader, adminUser)
850+
851+
rr := httptest.NewRecorder()
852+
ps := httprouter.Params{
853+
httprouter.Param{
854+
Key: NamespacePathParam,
855+
Value: namespaceNameCrud,
856+
},
857+
}
858+
a.CreateWorkspaceHandler(rr, req, ps)
859+
rs := rr.Result()
860+
defer rs.Body.Close()
861+
862+
Expect(rs.StatusCode).To(Equal(http.StatusCreated), descUnexpectedHTTPStatus, rr.Body.String())
863+
864+
// Get the created workspace
865+
createdWorkspace := &kubefloworgv1beta1.Workspace{}
866+
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: workspace.Name, Namespace: namespaceNameCrud}, createdWorkspace)).To(Succeed())
867+
868+
// TODO: Verify the secrets are properly set once #240 merged
869+
// Expect(createdWorkspace.Spec.PodTemplate.Volumes.Secrets).To(HaveLen(1))
870+
// Expect(createdWorkspace.Spec.PodTemplate.Volumes.Secrets[0].SecretName).To(Equal("test-secret"))
871+
// Expect(createdWorkspace.Spec.PodTemplate.Volumes.Secrets[0].MountPath).To(Equal("/secrets"))
872+
// Expect(createdWorkspace.Spec.PodTemplate.Volumes.Secrets[0].DefaultMode).To(Equal(int32(0o644)))
873+
})
874+
808875
// TODO: test when fail to create a Workspace when:
809876
// - body payload invalid (missing name/kind, and/or non RCF 1123 name)
810877
// - invalid namespace HTTP path parameter (also test for other API handlers)

workspaces/backend/internal/models/workspaces/types.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,9 @@ type PodMetadata struct {
6767
}
6868

6969
type PodVolumes struct {
70-
Home *PodVolumeInfo `json:"home,omitempty"`
71-
Data []PodVolumeInfo `json:"data"`
70+
Home *PodVolumeInfo `json:"home,omitempty"`
71+
Data []PodVolumeInfo `json:"data"`
72+
Secrets []PodSecretInfo `json:"secrets,omitempty"`
7273
}
7374

7475
type PodVolumeInfo struct {
@@ -77,6 +78,12 @@ type PodVolumeInfo struct {
7778
ReadOnly bool `json:"readOnly"`
7879
}
7980

81+
type PodSecretInfo struct {
82+
SecretName string `json:"secretName"`
83+
MountPath string `json:"mountPath"`
84+
DefaultMode int32 `json:"defaultMode,omitempty"`
85+
}
86+
8087
type PodTemplateOptions struct {
8188
ImageConfig ImageConfig `json:"imageConfig"`
8289
PodConfig PodConfig `json:"podConfig"`

workspaces/backend/internal/models/workspaces/types_create.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@ type PodMetadataMutate struct {
4343
}
4444

4545
type PodVolumesMutate struct {
46-
Home *string `json:"home,omitempty"`
47-
Data []PodVolumeMount `json:"data"`
46+
Home *string `json:"home,omitempty"`
47+
Data []PodVolumeMount `json:"data"`
48+
Secrets []PodSecretMount `json:"secrets,omitempty"`
4849
}
4950

5051
type PodVolumeMount struct {
@@ -53,6 +54,12 @@ type PodVolumeMount struct {
5354
ReadOnly bool `json:"readOnly,omitempty"`
5455
}
5556

57+
type PodSecretMount struct {
58+
SecretName string `json:"secretName"`
59+
MountPath string `json:"mountPath"`
60+
DefaultMode int32 `json:"defaultMode,omitempty"`
61+
}
62+
5663
type PodTemplateOptionsMutate struct {
5764
ImageConfig string `json:"imageConfig"`
5865
PodConfig string `json:"podConfig"`
@@ -87,5 +94,18 @@ func (w *WorkspaceCreate) Validate(prefix *field.Path) []*field.Error {
8794
errs = append(errs, helper.ValidateFieldIsNotEmpty(volumePath.Child("mountPath"), volume.MountPath)...)
8895
}
8996

97+
// validate the secrets
98+
secretsPath := prefix.Child("podTemplate", "volumes", "secrets")
99+
for i, secret := range w.PodTemplate.Volumes.Secrets {
100+
secretPath := secretsPath.Index(i)
101+
errs = append(errs, helper.ValidateFieldIsNotEmpty(secretPath.Child("secretName"), secret.SecretName)...)
102+
errs = append(errs, helper.ValidateFieldIsNotEmpty(secretPath.Child("mountPath"), secret.MountPath)...)
103+
if secret.DefaultMode != 0 {
104+
if secret.DefaultMode < 0 || secret.DefaultMode > 511 {
105+
errs = append(errs, field.Invalid(secretPath.Child("defaultMode"), secret.DefaultMode, "defaultMode must be between 0 and 511"))
106+
}
107+
}
108+
}
109+
90110
return errs
91111
}

workspaces/backend/internal/repositories/workspaces/repo.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,17 @@ func (r *WorkspaceRepository) CreateWorkspace(ctx context.Context, workspaceCrea
137137
}
138138
}
139139

140+
// get secrets from workspace model
141+
// TODO: uncomment this once #240 is merged
142+
// secretMounts := make([]kubefloworgv1beta1.PodSecretMount, len(workspaceCreate.PodTemplate.Volumes.Secrets))
143+
// for i, secret := range workspaceCreate.PodTemplate.Volumes.Secrets {
144+
// secretMounts[i] = kubefloworgv1beta1.PodSecretMount{
145+
// SecretName: secret.SecretName,
146+
// MountPath: secret.MountPath,
147+
// DefaultMode: secret.DefaultMode,
148+
// }
149+
// }
150+
140151
// define workspace object from model
141152
workspaceName := workspaceCreate.Name
142153
workspaceKindName := workspaceCreate.Kind
@@ -157,6 +168,7 @@ func (r *WorkspaceRepository) CreateWorkspace(ctx context.Context, workspaceCrea
157168
Volumes: kubefloworgv1beta1.WorkspacePodVolumes{
158169
Home: workspaceCreate.PodTemplate.Volumes.Home,
159170
Data: dataVolumeMounts,
171+
// Secrets: secretMounts,
160172
},
161173
Options: kubefloworgv1beta1.WorkspacePodOptions{
162174
ImageConfig: workspaceCreate.PodTemplate.Options.ImageConfig,

workspaces/backend/openapi/docs.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,34 @@ const docTemplate = `{
10721072
}
10731073
}
10741074
},
1075+
"workspaces.PodSecretInfo": {
1076+
"type": "object",
1077+
"properties": {
1078+
"defaultMode": {
1079+
"type": "integer"
1080+
},
1081+
"mountPath": {
1082+
"type": "string"
1083+
},
1084+
"secretName": {
1085+
"type": "string"
1086+
}
1087+
}
1088+
},
1089+
"workspaces.PodSecretMount": {
1090+
"type": "object",
1091+
"properties": {
1092+
"defaultMode": {
1093+
"type": "integer"
1094+
},
1095+
"mountPath": {
1096+
"type": "string"
1097+
},
1098+
"secretName": {
1099+
"type": "string"
1100+
}
1101+
}
1102+
},
10751103
"workspaces.PodTemplate": {
10761104
"type": "object",
10771105
"properties": {
@@ -1161,6 +1189,12 @@ const docTemplate = `{
11611189
},
11621190
"home": {
11631191
"$ref": "#/definitions/workspaces.PodVolumeInfo"
1192+
},
1193+
"secrets": {
1194+
"type": "array",
1195+
"items": {
1196+
"$ref": "#/definitions/workspaces.PodSecretInfo"
1197+
}
11641198
}
11651199
}
11661200
},
@@ -1175,6 +1209,12 @@ const docTemplate = `{
11751209
},
11761210
"home": {
11771211
"type": "string"
1212+
},
1213+
"secrets": {
1214+
"type": "array",
1215+
"items": {
1216+
"$ref": "#/definitions/workspaces.PodSecretMount"
1217+
}
11781218
}
11791219
}
11801220
},

workspaces/backend/openapi/swagger.json

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,34 @@
10701070
}
10711071
}
10721072
},
1073+
"workspaces.PodSecretInfo": {
1074+
"type": "object",
1075+
"properties": {
1076+
"defaultMode": {
1077+
"type": "integer"
1078+
},
1079+
"mountPath": {
1080+
"type": "string"
1081+
},
1082+
"secretName": {
1083+
"type": "string"
1084+
}
1085+
}
1086+
},
1087+
"workspaces.PodSecretMount": {
1088+
"type": "object",
1089+
"properties": {
1090+
"defaultMode": {
1091+
"type": "integer"
1092+
},
1093+
"mountPath": {
1094+
"type": "string"
1095+
},
1096+
"secretName": {
1097+
"type": "string"
1098+
}
1099+
}
1100+
},
10731101
"workspaces.PodTemplate": {
10741102
"type": "object",
10751103
"properties": {
@@ -1159,6 +1187,12 @@
11591187
},
11601188
"home": {
11611189
"$ref": "#/definitions/workspaces.PodVolumeInfo"
1190+
},
1191+
"secrets": {
1192+
"type": "array",
1193+
"items": {
1194+
"$ref": "#/definitions/workspaces.PodSecretInfo"
1195+
}
11621196
}
11631197
}
11641198
},
@@ -1173,6 +1207,12 @@
11731207
},
11741208
"home": {
11751209
"type": "string"
1210+
},
1211+
"secrets": {
1212+
"type": "array",
1213+
"items": {
1214+
"$ref": "#/definitions/workspaces.PodSecretMount"
1215+
}
11761216
}
11771217
}
11781218
},

workspaces/backend/openapi/swagger.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,24 @@ definitions:
356356
type: string
357357
type: object
358358
type: object
359+
workspaces.PodSecretInfo:
360+
properties:
361+
defaultMode:
362+
type: integer
363+
mountPath:
364+
type: string
365+
secretName:
366+
type: string
367+
type: object
368+
workspaces.PodSecretMount:
369+
properties:
370+
defaultMode:
371+
type: integer
372+
mountPath:
373+
type: string
374+
secretName:
375+
type: string
376+
type: object
359377
workspaces.PodTemplate:
360378
properties:
361379
options:
@@ -414,6 +432,10 @@ definitions:
414432
type: array
415433
home:
416434
$ref: '#/definitions/workspaces.PodVolumeInfo'
435+
secrets:
436+
items:
437+
$ref: '#/definitions/workspaces.PodSecretInfo'
438+
type: array
417439
type: object
418440
workspaces.PodVolumesMutate:
419441
properties:
@@ -423,6 +445,10 @@ definitions:
423445
type: array
424446
home:
425447
type: string
448+
secrets:
449+
items:
450+
$ref: '#/definitions/workspaces.PodSecretMount'
451+
type: array
426452
type: object
427453
workspaces.ProbeResult:
428454
enum:

0 commit comments

Comments
 (0)