Skip to content

Commit

Permalink
Make Medusa's config map be a merge of MedusaConfig object and cluste…
Browse files Browse the repository at this point in the history
…r spec
  • Loading branch information
rzvoncek committed Jan 29, 2024
1 parent 651e3f7 commit f25a17c
Show file tree
Hide file tree
Showing 12 changed files with 415 additions and 76 deletions.
1 change: 1 addition & 0 deletions CHANGELOG/CHANGELOG-1.12.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ When cutting a new release, update the `unreleased` heading to the tag being gen

## unreleased

* [CHANGE] [#1158](https://github.com/k8ssandra/k8ssandra-operator/issues/1158) Use the MedusaConfiguration API when creating Medusa configuration
* [CHANGE] [#1050](https://github.com/k8ssandra/k8ssandra-operator/issues/1050) Remove unnecessary requeues in the Medusa controllers
* [CHANGE] [#1165](https://github.com/k8ssandra/k8ssandra-operator/issues/1165) Upgrade to Medusa v0.17.1
* [FEATURE] [#1157](https://github.com/k8ssandra/k8ssandra-operator/issues/1157) Add the MedusaConfiguration API
Expand Down
11 changes: 11 additions & 0 deletions apis/k8ssandra/v1alpha1/k8ssandracluster_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ var (
ErrNoStorageConfig = fmt.Errorf("storageConfig must be defined at cluster level or dc level")
ErrNoResourcesSet = fmt.Errorf("softPodAntiAffinity requires Resources to be set")
ErrClusterName = fmt.Errorf("cluster name can not be changed")
ErrNoStoragePrefix = fmt.Errorf("medusa storage prefix must be set when a medusaConfigurationRef is used")
)

// log is for logging in this package.
Expand Down Expand Up @@ -88,6 +89,16 @@ func (r *K8ssandraCluster) validateK8ssandraCluster() error {
}
}

// Verify the Medusa storage prefix is explicitly set
// only relevant if Medusa is enabled and the MedusaConfiguration object is referenced
if r.Spec.Medusa != nil {
if r.Spec.Medusa.MedusaConfigurationRef.Name != "" {
if r.Spec.Medusa.StorageProperties.Prefix == "" {
return ErrNoStoragePrefix
}
}
}

if err := r.validateStatefulsetNameSize(); err != nil {
return err
}
Expand Down
44 changes: 44 additions & 0 deletions apis/k8ssandra/v1alpha1/k8ssandracluster_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/envtest"
logf "sigs.k8s.io/controller-runtime/pkg/log"

medusaapi "github.com/k8ssandra/k8ssandra-operator/apis/medusa/v1alpha1"
reaperapi "github.com/k8ssandra/k8ssandra-operator/apis/reaper/v1alpha1"
)

Expand Down Expand Up @@ -145,6 +146,7 @@ func TestWebhook(t *testing.T) {
t.Run("NumTokensValidation", testNumTokens)
t.Run("NumTokensValidationInUpdate", testNumTokensInUpdate)
t.Run("StsNameTooLong", testStsNameTooLong)
t.Run("MedusaPrefixMissing", testMedusaPrefixMissing)
}

func testContextValidation(t *testing.T) {
Expand Down Expand Up @@ -407,3 +409,45 @@ func createMinimalClusterObj(name, namespace string) *K8ssandraCluster {
},
}
}

func testMedusaPrefixMissing(t *testing.T) {
required := require.New(t)
createNamespace(required, "short-namespace")

clusterWithoutMedusa := createMinimalClusterObj("without-medusa", "short-namespace")
err := k8sClient.Create(ctx, clusterWithoutMedusa)
required.NoError(err)

clusterWithMedusa := createMinimalClusterObj("with-medusa", "short-namespace")
clusterWithMedusa.Spec.Medusa = &medusaapi.MedusaClusterTemplate{
StorageProperties: medusaapi.Storage{
Prefix: "",
},
}
err = k8sClient.Create(ctx, clusterWithMedusa)
required.NoError(err)

clusterWithoutPrefix := createMinimalClusterObj("without-prefix", "short-namespace")
clusterWithoutPrefix.Spec.Medusa = &medusaapi.MedusaClusterTemplate{
MedusaConfigurationRef: corev1.ObjectReference{
Name: "medusa-config",
},
StorageProperties: medusaapi.Storage{
Prefix: "",
},
}
err = k8sClient.Create(ctx, clusterWithoutPrefix)
required.Error(err)

clusterWithPrefix := createMinimalClusterObj("with-prefix", "short-namespace")
clusterWithPrefix.Spec.Medusa = &medusaapi.MedusaClusterTemplate{
MedusaConfigurationRef: corev1.ObjectReference{
Name: "medusa-config",
},
StorageProperties: medusaapi.Storage{
Prefix: "some-prefix",
},
}
err = k8sClient.Create(ctx, clusterWithPrefix)
required.NoError(err)
}
6 changes: 6 additions & 0 deletions apis/medusa/v1alpha1/medusa_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ type PodStorageSettings struct {
}

type MedusaClusterTemplate struct {
// MedusaConfigurationRef points to an existing MedusaConfiguration object.
// The purpose is to allow shared default settings across several clusters.
// StorageProperties override the settings from MedusaConfiguration object to allow customization.
// +optional
MedusaConfigurationRef corev1.ObjectReference `json:"medusaConfigurationRef,omitempty"`

// MedusaContainerImage is the image characteristics to use for Medusa containers. Leave nil
// to use a default image.
// +optional
Expand Down
1 change: 1 addition & 0 deletions apis/medusa/v1alpha1/zz_generated.deepcopy.go

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

40 changes: 40 additions & 0 deletions config/crd/bases/k8ssandra.io_k8ssandraclusters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27021,6 +27021,46 @@ spec:
format: int32
type: integer
type: object
medusaConfigurationRef:
description: MedusaConfigurationRef points to an existing MedusaConfiguration
object. The purpose is to allow shared default settings across
several clusters. StorageProperties override the settings from
MedusaConfiguration object to allow customization.
properties:
apiVersion:
description: API version of the referent.
type: string
fieldPath:
description: 'If referring to a piece of an object instead
of an entire object, this string should contain a valid
JSON/Go field access statement, such as desiredState.manifest.containers[2].
For example, if the object reference is to a container within
a pod, this would take on a value like: "spec.containers{name}"
(where "name" refers to the name of the container that triggered
the event) or if no container name is specified "spec.containers[2]"
(container with index 2 in this pod). This syntax is chosen
only to have some well-defined way of referencing a part
of an object. TODO: this design is not final and this field
is subject to change in the future.'
type: string
kind:
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
type: string
namespace:
description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
type: string
resourceVersion:
description: 'Specific resourceVersion to which this reference
is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
type: string
uid:
description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
type: string
type: object
x-kubernetes-map-type: atomic
readinessProbe:
description: Define the readiness probe settings to use for the
Medusa containers.
Expand Down
2 changes: 2 additions & 0 deletions controllers/k8ssandra/k8ssandracluster_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ func TestK8ssandraCluster(t *testing.T) {
t.Run("CreateMultiDcClusterWithStargate", testEnv.ControllerTest(ctx, createMultiDcClusterWithStargate))
t.Run("CreateMultiDcClusterWithReaper", testEnv.ControllerTest(ctx, createMultiDcClusterWithReaper))
t.Run("CreateMultiDcClusterWithMedusa", testEnv.ControllerTest(ctx, createMultiDcClusterWithMedusa))
t.Run("CreateSingleDcClusterWithMedusaConfigRef", testEnv.ControllerTest(ctx, createSingleDcClusterWithMedusaConfigRef))
t.Run("CreatingSingleDcClusterWithoutPrefixInClusterSpecFail", testEnv.ControllerTest(ctx, creatingSingleDcClusterWithoutPrefixInClusterSpecFails))
t.Run("CreateSingleDcClusterNoAuth", testEnv.ControllerTest(ctx, createSingleDcClusterNoAuth))
t.Run("CreateSingleDcClusterAuth", testEnv.ControllerTest(ctx, createSingleDcClusterAuth))
t.Run("CreateSingleDcClusterAuthExternalSecrets", testEnv.ControllerTest(ctx, createSingleDcClusterAuthExternalSecrets))
Expand Down
43 changes: 43 additions & 0 deletions controllers/k8ssandra/medusa_reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package k8ssandra
import (
"context"
"fmt"
"github.com/adutra/goalesce"
medusaapi "github.com/k8ssandra/k8ssandra-operator/apis/medusa/v1alpha1"
"k8s.io/apimachinery/pkg/types"

"github.com/go-logr/logr"
api "github.com/k8ssandra/k8ssandra-operator/apis/k8ssandra/v1alpha1"
Expand Down Expand Up @@ -32,6 +35,11 @@ func (r *K8ssandraClusterReconciler) reconcileMedusa(
if medusaSpec != nil {
logger.Info("Medusa is enabled")

mergeResult := mergeStorageProperties(ctx, remoteClient, namespace, medusaSpec, logger, kc)
if mergeResult.IsError() {
return result.Error(mergeResult.GetError())
}

Check warning on line 41 in controllers/k8ssandra/medusa_reconciler.go

View check run for this annotation

Codecov / codecov/patch

controllers/k8ssandra/medusa_reconciler.go#L40-L41

Added lines #L40 - L41 were not covered by tests

// Check that certificates are provided if client encryption is enabled
if cassandra.ClientEncryptionEnabled(dcConfig) {
if kc.Spec.UseExternalSecrets() {
Expand Down Expand Up @@ -203,3 +211,38 @@ func (r *K8ssandraClusterReconciler) reconcileMedusaConfigMap(
logger.Info("Medusa ConfigMap successfully reconciled")
return result.Continue()
}

func mergeStorageProperties(
ctx context.Context,
remoteClient client.Client,
namespace string,
medusaSpec *medusaapi.MedusaClusterTemplate,
logger logr.Logger,
desiredKc *api.K8ssandraCluster,
) result.ReconcileResult {
// check if the StorageProperties are defined in the K8ssandraCluster
if medusaSpec.MedusaConfigurationRef.Name == "" {
return result.Continue()
}
storageProperties := &medusaapi.MedusaConfiguration{}
configKey := types.NamespacedName{Namespace: namespace, Name: medusaSpec.MedusaConfigurationRef.Name}
if err := remoteClient.Get(ctx, configKey, storageProperties); err != nil {
logger.Error(err, fmt.Sprintf("failed to get MedusaConfiguration %s", configKey))
return result.Error(err)
}

Check warning on line 232 in controllers/k8ssandra/medusa_reconciler.go

View check run for this annotation

Codecov / codecov/patch

controllers/k8ssandra/medusa_reconciler.go#L230-L232

Added lines #L230 - L232 were not covered by tests
// check if the StorageProperties from the cluster have the prefix field set
// it is required to be present because that's the single thing that differentiates backups of two different clusters
if desiredKc.Spec.Medusa.StorageProperties.Prefix == "" {
return result.Error(fmt.Errorf("StorageProperties.Prefix is not set in K8ssandraCluster %s", utils.GetKey(desiredKc)))
}

Check warning on line 237 in controllers/k8ssandra/medusa_reconciler.go

View check run for this annotation

Codecov / codecov/patch

controllers/k8ssandra/medusa_reconciler.go#L236-L237

Added lines #L236 - L237 were not covered by tests
// try to merge the storage properties. goalesce gives priority to the 2nd argument,
// so stuff in the cluster overrides stuff in the config object
mergedProperties, err := goalesce.DeepMerge(storageProperties.Spec.StorageProperties, desiredKc.Spec.Medusa.StorageProperties)
if err != nil {
logger.Error(err, "failed to merge MedusaConfiguration StorageProperties")
return result.Error(err)
}

Check warning on line 244 in controllers/k8ssandra/medusa_reconciler.go

View check run for this annotation

Codecov / codecov/patch

controllers/k8ssandra/medusa_reconciler.go#L242-L244

Added lines #L242 - L244 were not covered by tests
// copy the merged properties back into the cluster
mergedProperties.DeepCopyInto(&desiredKc.Spec.Medusa.StorageProperties)
return result.Continue()
}
Loading

0 comments on commit f25a17c

Please sign in to comment.