fix(ws): add secrets field to backend API schema (#331)
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 <astonebe@redhat.com>
This commit is contained in:
parent
45d778a972
commit
ea93acc140
|
@ -770,6 +770,9 @@ var _ = Describe("Workspaces Handler", func() {
|
|||
}
|
||||
Expect(createdWorkspace.Spec.PodTemplate.Volumes.Data).To(Equal(expected))
|
||||
|
||||
// TODO: Verify secrets once #240 merged
|
||||
// Expect(createdWorkspace.Spec.PodTemplate.Volumes.Secrets).To(BeEmpty())
|
||||
|
||||
By("creating an HTTP request to delete the Workspace")
|
||||
path = strings.Replace(WorkspacesByNamePath, ":"+NamespacePathParam, namespaceNameCrud, 1)
|
||||
path = strings.Replace(path, ":"+ResourceNamePathParam, workspaceName, 1)
|
||||
|
@ -805,6 +808,70 @@ var _ = Describe("Workspaces Handler", func() {
|
|||
Expect(apierrors.IsNotFound(err)).To(BeTrue())
|
||||
})
|
||||
|
||||
// TODO: Change to It when #240 merged
|
||||
XIt("should create a workspace with secrets", func() {
|
||||
// Create a workspace with secrets
|
||||
workspace := &models.WorkspaceCreate{
|
||||
Name: "test-workspace",
|
||||
Kind: "test-kind",
|
||||
PodTemplate: models.PodTemplateMutate{
|
||||
Options: models.PodTemplateOptionsMutate{
|
||||
ImageConfig: "test-image",
|
||||
PodConfig: "test-config",
|
||||
},
|
||||
Volumes: models.PodVolumesMutate{
|
||||
Data: []models.PodVolumeMount{
|
||||
{
|
||||
PVCName: "test-pvc",
|
||||
MountPath: "/data",
|
||||
},
|
||||
},
|
||||
Secrets: []models.PodSecretMount{
|
||||
{
|
||||
SecretName: "test-secret",
|
||||
MountPath: "/secrets",
|
||||
DefaultMode: int32(0o644),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Create the workspace using the API handler
|
||||
bodyEnvelope := WorkspaceCreateEnvelope{Data: workspace}
|
||||
bodyEnvelopeJSON, err := json.Marshal(bodyEnvelope)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
path := strings.Replace(WorkspacesByNamespacePath, ":"+NamespacePathParam, namespaceNameCrud, 1)
|
||||
req, err := http.NewRequest(http.MethodPost, path, strings.NewReader(string(bodyEnvelopeJSON)))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set(userIdHeader, adminUser)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
ps := httprouter.Params{
|
||||
httprouter.Param{
|
||||
Key: NamespacePathParam,
|
||||
Value: namespaceNameCrud,
|
||||
},
|
||||
}
|
||||
a.CreateWorkspaceHandler(rr, req, ps)
|
||||
rs := rr.Result()
|
||||
defer rs.Body.Close()
|
||||
|
||||
Expect(rs.StatusCode).To(Equal(http.StatusCreated), descUnexpectedHTTPStatus, rr.Body.String())
|
||||
|
||||
// Get the created workspace
|
||||
createdWorkspace := &kubefloworgv1beta1.Workspace{}
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: workspace.Name, Namespace: namespaceNameCrud}, createdWorkspace)).To(Succeed())
|
||||
|
||||
// TODO: Verify the secrets are properly set once #240 merged
|
||||
// Expect(createdWorkspace.Spec.PodTemplate.Volumes.Secrets).To(HaveLen(1))
|
||||
// Expect(createdWorkspace.Spec.PodTemplate.Volumes.Secrets[0].SecretName).To(Equal("test-secret"))
|
||||
// Expect(createdWorkspace.Spec.PodTemplate.Volumes.Secrets[0].MountPath).To(Equal("/secrets"))
|
||||
// Expect(createdWorkspace.Spec.PodTemplate.Volumes.Secrets[0].DefaultMode).To(Equal(int32(0o644)))
|
||||
})
|
||||
|
||||
// TODO: test when fail to create a Workspace when:
|
||||
// - body payload invalid (missing name/kind, and/or non RCF 1123 name)
|
||||
// - invalid namespace HTTP path parameter (also test for other API handlers)
|
||||
|
|
|
@ -67,8 +67,9 @@ type PodMetadata struct {
|
|||
}
|
||||
|
||||
type PodVolumes struct {
|
||||
Home *PodVolumeInfo `json:"home,omitempty"`
|
||||
Data []PodVolumeInfo `json:"data"`
|
||||
Home *PodVolumeInfo `json:"home,omitempty"`
|
||||
Data []PodVolumeInfo `json:"data"`
|
||||
Secrets []PodSecretInfo `json:"secrets,omitempty"`
|
||||
}
|
||||
|
||||
type PodVolumeInfo struct {
|
||||
|
@ -77,6 +78,12 @@ type PodVolumeInfo struct {
|
|||
ReadOnly bool `json:"readOnly"`
|
||||
}
|
||||
|
||||
type PodSecretInfo struct {
|
||||
SecretName string `json:"secretName"`
|
||||
MountPath string `json:"mountPath"`
|
||||
DefaultMode int32 `json:"defaultMode,omitempty"`
|
||||
}
|
||||
|
||||
type PodTemplateOptions struct {
|
||||
ImageConfig ImageConfig `json:"imageConfig"`
|
||||
PodConfig PodConfig `json:"podConfig"`
|
||||
|
|
|
@ -43,8 +43,9 @@ type PodMetadataMutate struct {
|
|||
}
|
||||
|
||||
type PodVolumesMutate struct {
|
||||
Home *string `json:"home,omitempty"`
|
||||
Data []PodVolumeMount `json:"data"`
|
||||
Home *string `json:"home,omitempty"`
|
||||
Data []PodVolumeMount `json:"data"`
|
||||
Secrets []PodSecretMount `json:"secrets,omitempty"`
|
||||
}
|
||||
|
||||
type PodVolumeMount struct {
|
||||
|
@ -53,6 +54,12 @@ type PodVolumeMount struct {
|
|||
ReadOnly bool `json:"readOnly,omitempty"`
|
||||
}
|
||||
|
||||
type PodSecretMount struct {
|
||||
SecretName string `json:"secretName"`
|
||||
MountPath string `json:"mountPath"`
|
||||
DefaultMode int32 `json:"defaultMode,omitempty"`
|
||||
}
|
||||
|
||||
type PodTemplateOptionsMutate struct {
|
||||
ImageConfig string `json:"imageConfig"`
|
||||
PodConfig string `json:"podConfig"`
|
||||
|
@ -87,5 +94,18 @@ func (w *WorkspaceCreate) Validate(prefix *field.Path) []*field.Error {
|
|||
errs = append(errs, helper.ValidateFieldIsNotEmpty(volumePath.Child("mountPath"), volume.MountPath)...)
|
||||
}
|
||||
|
||||
// validate the secrets
|
||||
secretsPath := prefix.Child("podTemplate", "volumes", "secrets")
|
||||
for i, secret := range w.PodTemplate.Volumes.Secrets {
|
||||
secretPath := secretsPath.Index(i)
|
||||
errs = append(errs, helper.ValidateFieldIsNotEmpty(secretPath.Child("secretName"), secret.SecretName)...)
|
||||
errs = append(errs, helper.ValidateFieldIsNotEmpty(secretPath.Child("mountPath"), secret.MountPath)...)
|
||||
if secret.DefaultMode != 0 {
|
||||
if secret.DefaultMode < 0 || secret.DefaultMode > 511 {
|
||||
errs = append(errs, field.Invalid(secretPath.Child("defaultMode"), secret.DefaultMode, "defaultMode must be between 0 and 511"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
|
|
@ -137,6 +137,17 @@ func (r *WorkspaceRepository) CreateWorkspace(ctx context.Context, workspaceCrea
|
|||
}
|
||||
}
|
||||
|
||||
// get secrets from workspace model
|
||||
// TODO: uncomment this once #240 is merged
|
||||
// secretMounts := make([]kubefloworgv1beta1.PodSecretMount, len(workspaceCreate.PodTemplate.Volumes.Secrets))
|
||||
// for i, secret := range workspaceCreate.PodTemplate.Volumes.Secrets {
|
||||
// secretMounts[i] = kubefloworgv1beta1.PodSecretMount{
|
||||
// SecretName: secret.SecretName,
|
||||
// MountPath: secret.MountPath,
|
||||
// DefaultMode: secret.DefaultMode,
|
||||
// }
|
||||
// }
|
||||
|
||||
// define workspace object from model
|
||||
workspaceName := workspaceCreate.Name
|
||||
workspaceKindName := workspaceCreate.Kind
|
||||
|
@ -157,6 +168,7 @@ func (r *WorkspaceRepository) CreateWorkspace(ctx context.Context, workspaceCrea
|
|||
Volumes: kubefloworgv1beta1.WorkspacePodVolumes{
|
||||
Home: workspaceCreate.PodTemplate.Volumes.Home,
|
||||
Data: dataVolumeMounts,
|
||||
// Secrets: secretMounts,
|
||||
},
|
||||
Options: kubefloworgv1beta1.WorkspacePodOptions{
|
||||
ImageConfig: workspaceCreate.PodTemplate.Options.ImageConfig,
|
||||
|
|
|
@ -1072,6 +1072,34 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"workspaces.PodSecretInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"defaultMode": {
|
||||
"type": "integer"
|
||||
},
|
||||
"mountPath": {
|
||||
"type": "string"
|
||||
},
|
||||
"secretName": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"workspaces.PodSecretMount": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"defaultMode": {
|
||||
"type": "integer"
|
||||
},
|
||||
"mountPath": {
|
||||
"type": "string"
|
||||
},
|
||||
"secretName": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"workspaces.PodTemplate": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -1161,6 +1189,12 @@ const docTemplate = `{
|
|||
},
|
||||
"home": {
|
||||
"$ref": "#/definitions/workspaces.PodVolumeInfo"
|
||||
},
|
||||
"secrets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/workspaces.PodSecretInfo"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1175,6 +1209,12 @@ const docTemplate = `{
|
|||
},
|
||||
"home": {
|
||||
"type": "string"
|
||||
},
|
||||
"secrets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/workspaces.PodSecretMount"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1070,6 +1070,34 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"workspaces.PodSecretInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"defaultMode": {
|
||||
"type": "integer"
|
||||
},
|
||||
"mountPath": {
|
||||
"type": "string"
|
||||
},
|
||||
"secretName": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"workspaces.PodSecretMount": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"defaultMode": {
|
||||
"type": "integer"
|
||||
},
|
||||
"mountPath": {
|
||||
"type": "string"
|
||||
},
|
||||
"secretName": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"workspaces.PodTemplate": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -1159,6 +1187,12 @@
|
|||
},
|
||||
"home": {
|
||||
"$ref": "#/definitions/workspaces.PodVolumeInfo"
|
||||
},
|
||||
"secrets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/workspaces.PodSecretInfo"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1173,6 +1207,12 @@
|
|||
},
|
||||
"home": {
|
||||
"type": "string"
|
||||
},
|
||||
"secrets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/workspaces.PodSecretMount"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -356,6 +356,24 @@ definitions:
|
|||
type: string
|
||||
type: object
|
||||
type: object
|
||||
workspaces.PodSecretInfo:
|
||||
properties:
|
||||
defaultMode:
|
||||
type: integer
|
||||
mountPath:
|
||||
type: string
|
||||
secretName:
|
||||
type: string
|
||||
type: object
|
||||
workspaces.PodSecretMount:
|
||||
properties:
|
||||
defaultMode:
|
||||
type: integer
|
||||
mountPath:
|
||||
type: string
|
||||
secretName:
|
||||
type: string
|
||||
type: object
|
||||
workspaces.PodTemplate:
|
||||
properties:
|
||||
options:
|
||||
|
@ -414,6 +432,10 @@ definitions:
|
|||
type: array
|
||||
home:
|
||||
$ref: '#/definitions/workspaces.PodVolumeInfo'
|
||||
secrets:
|
||||
items:
|
||||
$ref: '#/definitions/workspaces.PodSecretInfo'
|
||||
type: array
|
||||
type: object
|
||||
workspaces.PodVolumesMutate:
|
||||
properties:
|
||||
|
@ -423,6 +445,10 @@ definitions:
|
|||
type: array
|
||||
home:
|
||||
type: string
|
||||
secrets:
|
||||
items:
|
||||
$ref: '#/definitions/workspaces.PodSecretMount'
|
||||
type: array
|
||||
type: object
|
||||
workspaces.ProbeResult:
|
||||
enum:
|
||||
|
|
Loading…
Reference in New Issue