Skip to content
This repository has been archived by the owner on Aug 1, 2024. It is now read-only.

[1.2.x] Allow configuring the storage class name for PVCs used by both DB and app #392

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .rhdh/bundle/manifests/rhdh-operator.csv.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,21 @@ spec:
displayName: Red Hat Developer Hub
kind: Backstage
name: backstages.rhdh.redhat.com
specDescriptors:
- description: Name of the storage class to use for the Persistent Volumes requested
by the application. The default storage class name will be used if this
field is not specified.
displayName: Storage Class for Application volumes
path: application.storageClassName
x-descriptors:
- urn:alm:descriptor:io.kubernetes:StorageClass
- description: Name of the storage class to use for the database Persistent
Volumes. The default storage class name will be used if this field is not
specified.
displayName: Storage Class for database volumes
path: database.storageClassName
x-descriptors:
- urn:alm:descriptor:io.kubernetes:StorageClass
version: v1alpha1
description: |
Red Hat Developer Hub is an enterprise-grade platform for building developer portals, containing a supported and opinionated framework. By implementing a unified and open platform designed to maximize developer skills, ease onboarding, and increase development productivity, focus can be centered on what really matters: writing great code. Red Hat Developer Hub also offers Software Templates to simplify the development process, which can reduce friction and frustration for development teams, boosting their productivity and increasing an organization's competitive advantage.
Expand Down
12 changes: 12 additions & 0 deletions api/v1alpha1/backstage_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ type Database struct {
// "POSTGRESQL_ADMIN_PASSWORD": "rl4s3Fh4ng3M4"
// "POSTGRES_HOST": "backstage-psql-bs1" # For local database, set to "backstage-psql-<CR name>".
AuthSecretName string `json:"authSecretName,omitempty"`

// Name of the storage class to use for the database Persistent Volumes.
// The default storage class name will be used if this field is not specified.
// +optional
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Storage Class for database volumes",xDescriptors={"urn:alm:descriptor:io.kubernetes:StorageClass"}
StorageClassName *string `json:"storageClassName,omitempty"`
}

type Application struct {
Expand Down Expand Up @@ -113,6 +119,12 @@ type Application struct {

// Route configuration. Used for OpenShift only.
Route *Route `json:"route,omitempty"`

// Name of the storage class to use for the Persistent Volumes requested by the application.
// The default storage class name will be used if this field is not specified.
// +optional
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Storage Class for Application volumes",xDescriptors={"urn:alm:descriptor:io.kubernetes:StorageClass"}
StorageClassName *string `json:"storageClassName,omitempty"`
}

type AppConfig struct {
Expand Down
12 changes: 11 additions & 1 deletion api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 16 additions & 1 deletion bundle/manifests/backstage-operator.clusterserviceversion.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ metadata:
}
]
capabilities: Seamless Upgrades
createdAt: "2024-07-04T10:22:58Z"
createdAt: "2024-07-05T09:05:48Z"
operatorframework.io/suggested-namespace: backstage-system
operators.operatorframework.io/builder: operator-sdk-v1.33.0
operators.operatorframework.io/project_layout: go.kubebuilder.io/v3
Expand All @@ -36,6 +36,21 @@ spec:
displayName: Backstage
kind: Backstage
name: backstages.rhdh.redhat.com
specDescriptors:
- description: Name of the storage class to use for the Persistent Volumes requested
by the application. The default storage class name will be used if this
field is not specified.
displayName: Storage Class for Application volumes
path: application.storageClassName
x-descriptors:
- urn:alm:descriptor:io.kubernetes:StorageClass
- description: Name of the storage class to use for the database Persistent
Volumes. The default storage class name will be used if this field is not
specified.
displayName: Storage Class for database volumes
path: database.storageClassName
x-descriptors:
- urn:alm:descriptor:io.kubernetes:StorageClass
version: v1alpha1
description: |
Operator to deploy Backstage on Kubernetes
Expand Down
10 changes: 10 additions & 0 deletions bundle/manifests/rhdh.redhat.com_backstages.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,11 @@ spec:
type: string
type: object
type: object
storageClassName:
description: Name of the storage class to use for the Persistent
Volumes requested by the application. The default storage class
name will be used if this field is not specified.
type: string
type: object
database:
description: Configuration for database access. Optional.
Expand All @@ -282,6 +287,11 @@ spec:
description: Control the creation of a local PostgreSQL DB. Set
to false if using for example an external Database for Backstage.
type: boolean
storageClassName:
description: Name of the storage class to use for the database
Persistent Volumes. The default storage class name will be used
if this field is not specified.
type: string
type: object
rawRuntimeConfig:
description: Raw Runtime RuntimeObjects configuration. For Advanced
Expand Down
10 changes: 10 additions & 0 deletions config/crd/bases/rhdh.redhat.com_backstages.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,11 @@ spec:
type: string
type: object
type: object
storageClassName:
description: Name of the storage class to use for the Persistent
Volumes requested by the application. The default storage class
name will be used if this field is not specified.
type: string
type: object
database:
description: Configuration for database access. Optional.
Expand All @@ -283,6 +288,11 @@ spec:
description: Control the creation of a local PostgreSQL DB. Set
to false if using for example an external Database for Backstage.
type: boolean
storageClassName:
description: Name of the storage class to use for the database
Persistent Volumes. The default storage class name will be used
if this field is not specified.
type: string
type: object
rawRuntimeConfig:
description: Raw Runtime RuntimeObjects configuration. For Advanced
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,21 @@ spec:
displayName: Backstage
kind: Backstage
name: backstages.rhdh.redhat.com
specDescriptors:
- description: Name of the storage class to use for the Persistent Volumes requested
by the application. The default storage class name will be used if this
field is not specified.
displayName: Storage Class for Application volumes
path: application.storageClassName
x-descriptors:
- urn:alm:descriptor:io.kubernetes:StorageClass
- description: Name of the storage class to use for the database Persistent
Volumes. The default storage class name will be used if this field is not
specified.
displayName: Storage Class for database volumes
path: database.storageClassName
x-descriptors:
- urn:alm:descriptor:io.kubernetes:StorageClass
version: v1alpha1
description: |
Operator to deploy Backstage on Kubernetes
Expand Down
7 changes: 7 additions & 0 deletions pkg/model/db-statefulset.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ func (b *DbStatefulSet) validate(model *BackstageModel, backstage bsv1alpha1.Bac
} else if model.LocalDbSecret != nil {
utils.SetDbSecretEnvVar(b.container(), model.LocalDbSecret.secret.Name)
}

if backstage.Spec.Database != nil && backstage.Spec.Database.StorageClassName != nil {
for i := range b.statefulSet.Spec.VolumeClaimTemplates {
b.statefulSet.Spec.VolumeClaimTemplates[i].Spec.StorageClassName = backstage.Spec.Database.StorageClassName
}
}

return nil
}

Expand Down
35 changes: 35 additions & 0 deletions pkg/model/db-statefulset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,38 @@ func TestImagePullSecretSpec(t *testing.T) {

assert.Equal(t, 0, len(model.localDbStatefulSet.statefulSet.Spec.Template.Spec.ImagePullSecrets))
}

func TestSpecDatabaseStorageClassName(t *testing.T) {
b := *dbStatefulSetBackstage.DeepCopy()
b.Spec.Database.StorageClassName = ptr.To("my-db-storage-class-1")

testObj := createBackstageTest(b).withDefaultConfig(true)

model, err := InitObjects(context.TODO(), b, testObj.externalConfig, true, true, testObj.scheme)
assert.NoError(t, err)

assert.NotNil(t, model.localDbStatefulSet)
assert.NotNil(t, model.localDbStatefulSet.statefulSet)
assert.Len(t, model.localDbStatefulSet.statefulSet.Spec.VolumeClaimTemplates, 1)
vol := model.localDbStatefulSet.statefulSet.Spec.VolumeClaimTemplates[0]
sc := vol.Spec.StorageClassName
assert.NotNil(t, sc)
assert.Equal(t, "my-db-storage-class-1", *sc)

// Set it to an empty string and make sure storage class is effectively empty (not the defaults)
b = *dbStatefulSetBackstage.DeepCopy()
b.Spec.Database.StorageClassName = ptr.To("")

testObj = createBackstageTest(b).withDefaultConfig(true)

model, err = InitObjects(context.TODO(), b, testObj.externalConfig, true, true, testObj.scheme)
assert.NoError(t, err)

assert.NotNil(t, model.localDbStatefulSet)
assert.NotNil(t, model.localDbStatefulSet.statefulSet)
assert.Len(t, model.localDbStatefulSet.statefulSet.Spec.VolumeClaimTemplates, 1)
vol = model.localDbStatefulSet.statefulSet.Spec.VolumeClaimTemplates[0]
sc = vol.Spec.StorageClassName
assert.NotNil(t, sc)
assert.Empty(t, *sc)
}
8 changes: 8 additions & 0 deletions pkg/model/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,14 @@ func (b *BackstageDeployment) validate(model *BackstageModel, backstage bsv1alph
utils.SetDbSecretEnvVar(b.container(), model.LocalDbSecret.secret.Name)
}

if backstage.Spec.Application != nil && backstage.Spec.Application.StorageClassName != nil {
for i, v := range b.deployment.Spec.Template.Spec.Volumes {
if v.Ephemeral != nil && v.Ephemeral.VolumeClaimTemplate != nil {
b.deployment.Spec.Template.Spec.Volumes[i].Ephemeral.VolumeClaimTemplate.Spec.StorageClassName = backstage.Spec.Application.StorageClassName
}
}
}

return nil
}

Expand Down
41 changes: 41 additions & 0 deletions pkg/model/deployment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,44 @@ func TestSpecImagePullSecrets(t *testing.T) {
assert.Equal(t, 0, len(model.backstageDeployment.deployment.Spec.Template.Spec.ImagePullSecrets))

}

func TestSpecApplicationStorageClassName(t *testing.T) {
b := *deploymentTestBackstage.DeepCopy()
b.Spec.Application.StorageClassName = ptr.To("my-storage-class-1")

testObj := createBackstageTest(b).withDefaultConfig(true).
addToDefaultConfig("deployment.yaml", "janus-deployment.yaml")

model, err := InitObjects(context.TODO(), b, testObj.externalConfig, true, true, testObj.scheme)
assert.NoError(t, err)

assert.NotNil(t, model.backstageDeployment)
assert.NotNil(t, model.backstageDeployment.deployment)
assert.Len(t, model.backstageDeployment.deployment.Spec.Template.Spec.Volumes, 2)
vol := model.backstageDeployment.deployment.Spec.Template.Spec.Volumes[0]
assert.NotNil(t, vol.Ephemeral)
assert.NotNil(t, vol.Ephemeral.VolumeClaimTemplate)
sc := vol.Ephemeral.VolumeClaimTemplate.Spec.StorageClassName
assert.NotNil(t, sc)
assert.Equal(t, "my-storage-class-1", *sc)

// Set it to an empty string and make sure storage class is effectively empty (not the defaults)
b = *deploymentTestBackstage.DeepCopy()
b.Spec.Application.StorageClassName = ptr.To("")

testObj = createBackstageTest(b).withDefaultConfig(true).
addToDefaultConfig("deployment.yaml", "janus-deployment.yaml")

model, err = InitObjects(context.TODO(), b, testObj.externalConfig, true, true, testObj.scheme)
assert.NoError(t, err)

assert.NotNil(t, model.backstageDeployment)
assert.NotNil(t, model.backstageDeployment.deployment)
assert.Len(t, model.backstageDeployment.deployment.Spec.Template.Spec.Volumes, 2)
vol = model.backstageDeployment.deployment.Spec.Template.Spec.Volumes[0]
assert.NotNil(t, vol.Ephemeral)
assert.NotNil(t, vol.Ephemeral.VolumeClaimTemplate)
sc = vol.Ephemeral.VolumeClaimTemplate.Spec.StorageClassName
assert.NotNil(t, sc)
assert.Empty(t, *sc)
}