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

Standalone medusa pod #980

Merged
merged 15 commits into from
Jun 21, 2023
Merged

Standalone medusa pod #980

merged 15 commits into from
Jun 21, 2023

Conversation

adejanovski
Copy link
Contributor

@adejanovski adejanovski commented May 24, 2023

What this PR does:
The creation of the main Medusa container has been separated from its update process. The medusa.CreateMedusaMainContainer method now handles creation, while medusa.UpdateMedusaMainContainer only updates the created container.

The creation and updating of Medusa volumes has been improved. Volumes are generated using the medusa.GenerateMedusaVolumes method and then updated or added to the CassandraDatacenter configuration (dcConfig).

Both these changes allow creating a standalone Medusa pod that is using the medusa.StandaloneMedusaDeployment method, by reusing the previously created Medusa main container and volumes.

14/6/23 update: The local storage option for Medusa has been removed. It was only available for testing purposes but since we now use a separate pod to compute restore mappings, local storage is no longer an option. As such, there are no more additionalVolumes added to the CassandraDatacenter definition as part of reconciling Medusa. This is a breaking change.

A standalone Medusa service is also created and reconciled. The medusa.StandaloneMedusaService method creates the service, which is then reconciled like other resources.

The reconciliation process for the Medusa ConfigMap has been updated. It now includes a key to the K8ssandraCluster resource, which can be used for owner references or other purposes.
Support was added for the definition of readiness and liveness probe settings for Medusa containers. This allows developers to specify custom probe parameters, such as initialDelaySeconds, periodSeconds, failureThreshold, successThreshold, and timeoutSeconds.
In the cleanup.go file, the checkDeletion function now includes calls to the newly introduced deleteDeployments and deleteServices methods. These methods aim to delete all Kubernetes Services and Deployments associated with the K8ssandraCluster in question. If the deletion of any of these resources fails, an error is logged and the function will return true, indicating the presence of errors.
The deleteServices method lists all services part of the K8ssandraCluster, as determined by label selectors, and attempts to delete them. Similarly, the deleteDeployments method lists and tries to delete all deployments associated with a deleted K8ssandraCluster.

This pull request also introduces an enhancement to the generic reconciliation process. The changes aim to improve the labeling of reconciled objects, linking them back to the associated K8ssandra object:
The ReconcileObject function signature has been updated to take an additional parameter, klKey. This parameter represents the key of a K8ssandra object, which includes its name and namespace.
Inside the ReconcileObject function, if a klKey is provided, the reconciled object is given a new set of labels. These labels are generated using the labels.PartOfLabels function with the provided klKey. These labels signify that the reconciled object is part of the associated K8ssandra object which then allows proper cleanup of resources upon deletion of the K8ssandraCluster object.

Which issue(s) this PR fixes:
Fixes #483
Fixes #486
Fixes #821
Fixes #990

Checklist

  • Changes manually tested
  • Automated Tests added/updated
  • Documentation added/updated
  • CHANGELOG.md updated (not required for documentation PRs)
  • CLA Signed: DataStax CLA

@adejanovski adejanovski mentioned this pull request May 24, 2023
5 tasks
@codecov
Copy link

codecov bot commented May 25, 2023

Codecov Report

Merging #980 (d3a50fc) into main (e486960) will decrease coverage by 0.47%.
The diff coverage is 48.68%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #980      +/-   ##
==========================================
- Coverage   57.91%   57.45%   -0.47%     
==========================================
  Files          98       99       +1     
  Lines        9655    10013     +358     
==========================================
+ Hits         5592     5753     +161     
- Misses       3594     3767     +173     
- Partials      469      493      +24     
Impacted Files Coverage Δ
apis/medusa/v1alpha1/medusarestorejob_types.go 100.00% <ø> (ø)
pkg/medusa/grpc.go 0.00% <0.00%> (ø)
pkg/medusa/requests.go 0.00% <0.00%> (ø)
...elemetry/cassandra_agent/cassandra_agent_config.go 22.22% <0.00%> (+0.34%) ⬆️
pkg/medusa/reconcile.go 56.62% <34.93%> (-3.16%) ⬇️
controllers/k8ssandra/cleanup.go 54.94% <41.66%> (-2.61%) ⬇️
pkg/cassandra/datacenter.go 66.37% <50.00%> (-0.20%) ⬇️
pkg/medusa/hostmap.go 53.61% <53.61%> (ø)
controllers/medusa/medusarestorejob_controller.go 56.61% <58.69%> (-3.89%) ⬇️
controllers/k8ssandra/medusa_reconciler.go 71.66% <74.13%> (-2.58%) ⬇️
... and 1 more

... and 3 files with indirect coverage changes

Copy link
Member

@Miles-Garnsey Miles-Garnsey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've left a few comments, not yet sure how to test this or whether you're ready for me to kick off manual tests.

Suggestion: I have one more general comment too, which is non-blocking, but hrmmmm... The way you're creating the Medusa containers/deployment feels convoluted/tortured to me. Each of them seems to go through multiple phases of mutation, e.g. UpdateMedusaInitContainer, medusaInitContainerResources, CreateMedusaMainContainer, setImage, medusaVolumeMounts, GenerateMedusaVolumes, medusaEnvVars, etc. This is less declarative than I sense it could be.

Issue: There are also code paths that may not be needed anymore. E.g. do we still allow local storage here?

Suggestion: Maybe Medusa just has a lot of configurables, but I wonder if better API design on the CRD (so that it uses more k8s native types) together with more extensive usage of goalesce might allow for better configurability while simplifying the reconciliation code paths?

pkg/reconciliation/generic.go Outdated Show resolved Hide resolved
CHANGELOG/CHANGELOG-1.7.md Show resolved Hide resolved
controllers/k8ssandra/cleanup.go Show resolved Hide resolved
controllers/k8ssandra/cleanup.go Show resolved Hide resolved
controllers/k8ssandra/medusa_reconciler.go Show resolved Hide resolved
pkg/medusa/reconcile.go Show resolved Hide resolved
adejanovski and others added 6 commits June 13, 2023 19:10
…ing part of labels directly

Fix restore hostmap test

Temporarily disable flaky test

Replace the prepare_restore tasks with an internal computation of the restore mapping

Use the new Medusa build

Fix failing unit tests

Fix tests with use of the right context for Medusa Standalone depl checks

Change Medusa probe settings to use an actual Probe object and using goalesce to merge with the defaults

Revert adding labels in the generic reconciler
@adejanovski adejanovski force-pushed the standalone-medusa-pod branch from cb28725 to d8b649b Compare June 13, 2023 17:12
@adejanovski adejanovski force-pushed the standalone-medusa-pod branch from 22abdd4 to df63afe Compare June 14, 2023 09:39
@adejanovski adejanovski marked this pull request as ready for review June 14, 2023 13:37
@adejanovski adejanovski requested a review from a team as a code owner June 14, 2023 13:37
},
Spec: appsv1.DeploymentSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"app": medusa.MedusaStandaloneDeploymentName(kc.SanitizedName(), dcName)},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little confused about this one, shouldn't the kc.SanitizedName just be kc.meta.name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should not. We name dependent objects based on the sanitized name, not the kc name. This way the objects have the same prefix names as the Cassandra statefulset pods.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding was that the SanitizedName was for getting a DNS-compatible k8s resource name from a Cassandra cluster name (which can contain more characters). But we should be naming dependant objects based on the k8s resource names, shouldn't we?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed offline, this is not the path we took with cluster/dc name overrides.
The SanitizedName will pick the override if it exists and sanitize it, or the resource name if not.
And all dependent objects will be named based on this.

// It uses the Medusa client to get the host map for the restore operation, using the Medusa standalone service to get the backup topology.
func (r *MedusaRestoreJobReconciler) prepareRestore(ctx context.Context, request *medusa.RestoreRequest) (*medusav1alpha1.MedusaRestoreMapping, error) {
medusaClient, err := r.ClientFactory.NewClient(medusaServiceUrl(
request.Datacenter.Spec.ClusterName,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Observation: We really need to start distinguishing between the ClusterName for the k8s resource and the Cassandra cluster name. Impossible to tell what is going on throughout these codebases sometimes :/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: The right label here is thought: instead of Observation: 😉

The backup and restore operations are directly related to a CassandraDatacenter object, they have no connection to a K8ssandraCluster object. The only way to get the cluster name, which is used to build the service url, is .spec.ClusterName here.

I understand there can be some confusion, but both options we had did come with their share of confusion, we just picked one over the other 😅

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only way to get the cluster name, which is used to build the service url, is .spec.ClusterName here.

Do you mean .spec.cassandra.clusterName? This can be set differently to the k8s resource name and doesn't need to bear any relation to it AFAIK.

I believe you could possibly use .metadata.labels to get the parent cluster name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conversation resolved offline: we don't want to use the k8c resource name here, we really need the sanitized name.

@@ -29,3 +35,27 @@ func GetDatacenterForDecommission(kc *api.K8ssandraCluster) string {

return ""
}

func GetK8ssandraClusterFromCassDc(ctx context.Context, cassdc *cassdcapi.CassandraDatacenter, client client.Client, logger logr.Logger) *api.K8ssandraCluster {
Copy link
Member

@Miles-Garnsey Miles-Garnsey Jun 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue: this is wrong, and is causing the bug I found in testing. See my testing comments for more details.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good catch!!
We need to use the sanitized names instead for both the cluster and the dc, otherwise we'll end up with either the wrong name or a name that's incompatible with kubernetes.
I'll fix this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comments above, I suspect we need to discuss the use of sanitizedName and similar in this PR. I think what you need to use is actually the cluster name from the labels throughout.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

even worse, this is dead code :)

I'm removing this.

@Miles-Garnsey
Copy link
Member

I've left a number of comments above RE the implementation details. Black box testing reveals a bug.

Procedure

  1. I created a cluster with medusa options specified and three racks [rack1,rack2,rack3], clusterName: "First Cluster", DC name firstdc, K8ssandraCluster k8s resource name test. I also created the minio deployment, and minio setup job.
  2. I created a few dummy tables/keyspaces.
  3. I ran nodetool flush on one node.
  4. I created a backup against that cluster which ran successfully (based on the presence of a finished time).
  5. I deleted that cluster and created a new one with the same k8s resource name but .spec.cassandra.clusterName: "Second Cluster".

I got the following error back from the k8ssandra-operator pod:

2023-06-19T06:14:45.508Z    ERROR Reconciler error {"controller": "medusarestorejob", "controllerGroup": "medusa.k8ssandra.io", "controllerKind": "MedusaRestoreJob", "MedusaRestoreJob": {"name":"restore-backup1","namespace":"k8ssandra-operator"}, "namespace": "k8ssandra-operator", "name": "restore-backup1", "reconcileID": "e313db82-43e8-4791-834c-6bf1b8aaff90", "error": "failed to create gRPC connection to Second Cluster-seconddc-medusa-service.k8ssandra-operator.svc:50051: context deadline exceeded"}

Somewhere (I think perhaps here) in the codebase you're using the clusterName field when you should be using the k8s resource name instead.

@sonarqubecloud
Copy link

SonarCloud Quality Gate failed.    Quality Gate failed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 14 Code Smells

No Coverage information No Coverage information
6.6% 6.6% Duplication

idea Catch issues before they fail your Quality Gate with our IDE extension sonarlint SonarLint

@Miles-Garnsey
Copy link
Member

Oops, looks like I approved the old PR. My comments were as follows:

@adejanovski, as discussed, it does appear that the sanitized clusterName field is the point of reference for downstream resource names, so that's all good. Apologies for my error there.

I've raised a ticket RE using goalesce to disentangle things a bit here, but as it is an implementation detail I won't block merge on this point.

I tested once again, changing the k8s resource name, cluster name, and rack names (but leaving the DC name unchanged).

That tests shows that I can use cqlsh to extract data added to the original cluster from the restored cluster. So I think this is good to merge. Approving once again. Nice work on getting this monster across the line!

Approving this one now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants