-
Notifications
You must be signed in to change notification settings - Fork 19
(feat): Extra Valkey config #44
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
base: main
Are you sure you want to change the base?
Changes from all commits
a05d5d6
7241a35
05af544
8919932
f066a91
cc614b7
88cf17a
02808a3
59bcd2e
3a1709a
8dce649
26e809e
f487a3c
7780156
3db1fd2
cc6b210
28712d8
8a75a87
a2c1818
3736d67
925e406
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,188 @@ | ||
| /* | ||
| Copyright 2025 Valkey Contributors. | ||
|
|
||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. | ||
| You may obtain a copy of the License at | ||
|
|
||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. | ||
| */ | ||
|
|
||
| package controller | ||
|
|
||
| import ( | ||
| "context" | ||
| "crypto/sha256" | ||
| "embed" | ||
| "fmt" | ||
| "maps" | ||
| "slices" | ||
|
|
||
| corev1 "k8s.io/api/core/v1" | ||
| apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| "k8s.io/apimachinery/pkg/types" | ||
| "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" | ||
| logf "sigs.k8s.io/controller-runtime/pkg/log" | ||
| valkeyiov1alpha1 "valkey.io/valkey-operator/api/v1alpha1" | ||
| ) | ||
|
|
||
| const ( | ||
| configHashKey = "valkey.io/config-hash" | ||
| configFileKey = "valkey.conf" | ||
| ) | ||
|
|
||
| //go:embed scripts/* | ||
| var scripts embed.FS | ||
|
|
||
| func getConfigMapName(clusterName string) string { | ||
| return clusterName + "-config" | ||
| } | ||
|
|
||
| // Create or update a default valkey.conf | ||
| // If additional config is provided, append to the default map | ||
| func (r *ValkeyClusterReconciler) upsertConfigMap(ctx context.Context, cluster *valkeyiov1alpha1.ValkeyCluster) error { | ||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we rename this file to configmap.go
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point |
||
| log := logf.FromContext(ctx) | ||
|
|
||
| // Hash writer for embedded scripts, and configuration parameters | ||
| hashConfigContents := sha256.New() | ||
|
|
||
| // Embed readiness check script | ||
| readiness, err := scripts.ReadFile("scripts/readiness-check.sh") | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know if this is nitpicking, but does it make sense to have scripts in a configmap named I would argue to keep
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is your suggestion to move the scripts to their own configMap? I have no objection with that.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's fine to leave as is. We can refactor later on |
||
| if err != nil { | ||
| return err | ||
| } | ||
| hashConfigContents.Write(readiness) | ||
|
|
||
| // Embed liveness check script | ||
| liveness, err := scripts.ReadFile("scripts/liveness-check.sh") | ||
| if err != nil { | ||
| return err | ||
| } | ||
| hashConfigContents.Write(liveness) | ||
|
|
||
| // Base config | ||
| serverConfig := `# Base operator config | ||
| cluster-enabled yes | ||
| protected-mode no | ||
| cluster-node-timeout 2000 | ||
| ` | ||
|
|
||
| // Local copy | ||
| specConfig := cluster.Spec.ValkeySpec.Configuration | ||
|
|
||
| // If there are any user-defined config parameters | ||
| if len(specConfig) > 0 { | ||
|
|
||
| // Sort the config keys to keep consistent processing order | ||
| sortedKeys := slices.Sorted(maps.Keys(specConfig)) | ||
|
|
||
| // Build the config | ||
| serverConfig += "\n# Extra config\n" | ||
| for _, k := range sortedKeys { | ||
|
|
||
| if slices.Contains(valkeyiov1alpha1.NonUserOverrideConfigParameters, k) { | ||
| log.Error(nil, "Prohibited valkey server config", "parameter", k) | ||
| r.Recorder.Eventf(cluster, nil, corev1.EventTypeWarning, "ConfigMapUpdateFailed", "UpsertConfigMap", "Prohibited config: %v", k) | ||
| continue | ||
| } | ||
|
|
||
| serverConfig += k + " " + specConfig[k] + "\n" | ||
| } | ||
| } | ||
|
Comment on lines
+82
to
+99
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could this be extracted into its own function? Something like: if len(cluster.Spec.ValkeySpec.Configuration) > 0 {
appendSpecConfig(&serverConfig, cluster.Spec.ValkeySpec.Configuration)
}
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess it could. What's the benefit though of having the same code but in another function? Doesn't that introduce additional code jumps?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The benefit is readability - ideally we want to keep isolated units of work behind functions that are named well so that when reading the entirety of |
||
|
|
||
| // Look for, and fetch existing configMap for this cluster | ||
| serverConfigMapName := getConfigMapName(cluster.Name) | ||
| needCreateConfigMap := false | ||
|
|
||
| serverConfigMap := &corev1.ConfigMap{} | ||
| if err := r.Get(ctx, types.NamespacedName{ | ||
| Name: serverConfigMapName, | ||
| Namespace: cluster.Namespace, | ||
| }, serverConfigMap); err != nil { | ||
| if !apierrors.IsNotFound(err) { | ||
| log.Error(err, "failed to fetch server configmap") | ||
| return err | ||
| } | ||
|
|
||
| // ConfigMap not found. Create configMap object with contents | ||
| needCreateConfigMap = true | ||
| log.V(2).Info("creating server configMap", "name", serverConfigMapName) | ||
|
|
||
| serverConfigMap.ObjectMeta = metav1.ObjectMeta{ | ||
| Name: serverConfigMapName, | ||
| Namespace: cluster.Namespace, | ||
| Labels: labels(cluster), | ||
| } | ||
| serverConfigMap.Data = map[string]string{ | ||
| "readiness-check.sh": string(readiness), | ||
| "liveness-check.sh": string(liveness), | ||
| configFileKey: serverConfig, | ||
| } | ||
| } | ||
|
|
||
| // Register ownership of the configMap | ||
| if err := controllerutil.SetControllerReference(cluster, serverConfigMap, r.Scheme); err != nil { | ||
| log.Error(err, "Failed to grab ownership of server configMap") | ||
| r.Recorder.Eventf(cluster, nil, corev1.EventTypeWarning, "ConfigMapCreationFailed", "UpsertConfigMap", "Failed to grab ownership of server configMap: %v", err) | ||
| return err | ||
| } | ||
|
|
||
| // Calculate hash of existing config parameters | ||
| origServerConfigHash := fmt.Sprintf("%x", sha256.Sum256([]byte(serverConfigMap.Data[configFileKey]))) | ||
|
|
||
utdrmac marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // Calculate hash of new config parameters | ||
| hashConfigContents.Write([]byte(serverConfig)) | ||
| newServerConfigHash := fmt.Sprintf("%x", hashConfigContents.Sum(nil)) | ||
|
|
||
| // Was the configMap changed? This is an invalid action, as users should modify | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a bit hard to follow with a lot of flags (maybe I need more coffee..) |
||
| // the ValkeyCluster CR to change parameters, not the configMap itself. | ||
| origConfigModified := !hasAnnotation(serverConfigMap, configHashKey, origServerConfigHash) | ||
|
|
||
| // Compare hash to the one already attached to the configMap (if previously exists) | ||
| needsUpdate := upsertAnnotation(serverConfigMap, configHashKey, newServerConfigHash) | ||
|
|
||
| // Original config is not modified, and new config doesn't change anything | ||
| if !origConfigModified && !needsUpdate { | ||
| log.V(1).Info("server config unchanged") | ||
| return nil | ||
| } | ||
|
|
||
| // In all other cases (ie: user modified configMap, or modified CR), | ||
| // update the config with the newly changed config. Also sync the | ||
| // check scripts in case they are updated in a later operator version. | ||
| serverConfigMap.Data["readiness-check.sh"] = string(readiness) | ||
| serverConfigMap.Data["liveness-check.sh"] = string(liveness) | ||
| serverConfigMap.Data[configFileKey] = serverConfig | ||
|
|
||
| // Need to create new configMap | ||
| if needCreateConfigMap { | ||
| if err := r.Create(ctx, serverConfigMap); err != nil { | ||
| log.Error(err, "Failed to create server configMap") | ||
| r.Recorder.Eventf(cluster, nil, corev1.EventTypeWarning, "ConfigMapCreationFailed", "UpsertConfigMap", "Failed to create server configMap: %v", err) | ||
| return err | ||
| } else { | ||
| r.Recorder.Eventf(cluster, nil, corev1.EventTypeNormal, "ConfigMapCreated", "UpsertConfigMap", "Created server configMap") | ||
| return nil | ||
sandeepkunusoth marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
|
|
||
| // Otherwise, update it | ||
| if err := r.Update(ctx, serverConfigMap); err != nil { | ||
| log.Error(err, "Failed to update server configMap") | ||
| r.Recorder.Eventf(cluster, nil, corev1.EventTypeWarning, "ConfigMapUpdateFailed", "UpsertConfigMap", "Failed to update server configMap: %v", err) | ||
| return err | ||
| } | ||
|
|
||
| r.Recorder.Eventf(cluster, nil, corev1.EventTypeNormal, "ConfigMapUpdated", "UpsertConfigMap", "Synchronized server configMap") | ||
|
|
||
| // All is good. Server configMap will be auto-mounted in the deployment | ||
| return nil | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i dont think updated configmap will be auto mounted in the existing deployments during update. Did u test this
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seem to update the configfile in the pods when I test it on But, I now see that we are not using
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updating the configmap is one thing, but the Valkey instances will not use these updates anyway in the current form. Is that what you where thinking on @sandeepkunusoth ?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree that we should omit live applying changes to the config for another PR |
||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.