Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use the MedusaConfiguration API when creating Medusa configuration #1187

Merged
merged 1 commit into from
Jan 29, 2024
Merged
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
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
46 changes: 45 additions & 1 deletion 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 All @@ -21,17 +24,23 @@ import (
// Create all things Medusa related in the cassdc podTemplateSpec
func (r *K8ssandraClusterReconciler) reconcileMedusa(
ctx context.Context,
kc *api.K8ssandraCluster,
desiredKc *api.K8ssandraCluster,
dcConfig *cassandra.DatacenterConfig,
remoteClient client.Client,
logger logr.Logger,
) result.ReconcileResult {
kc := desiredKc.DeepCopy()
namespace := utils.FirstNonEmptyString(dcConfig.Meta.Namespace, kc.Namespace)
logger.Info("Medusa reconcile for " + dcConfig.CassDcName() + " on namespace " + namespace)
medusaSpec := kc.Spec.Medusa
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 that certificates are provided if client encryption is enabled
if cassandra.ClientEncryptionEnabled(dcConfig) {
if kc.Spec.UseExternalSecrets() {
Expand Down Expand Up @@ -203,3 +212,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 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)))
}
// 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)
}
// copy the merged properties back into the cluster
mergedProperties.DeepCopyInto(&desiredKc.Spec.Medusa.StorageProperties)
return result.Continue()
}
Loading
Loading