Skip to content
Draft
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
19 changes: 13 additions & 6 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ rules:
- patch
- update
- watch
- apiGroups:
- ""
resources:
- configmaps
verbs:
- delete
- get
- list
- watch
- apiGroups:
- ""
resources:
Expand All @@ -28,16 +37,15 @@ rules:
- apiGroups:
- ""
resources:
- services
- configmaps
- secrets
- services
verbs:
- get
- patch
- update
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- perses.dev
Expand Down Expand Up @@ -143,4 +151,3 @@ rules:
- get
- patch
- update

7 changes: 5 additions & 2 deletions controllers/perses/perses_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ var log = logger.WithField("module", "perses_controller")
// +kubebuilder:rbac:groups=core,resources=events,verbs=create;patch
// +kubebuilder:rbac:groups=apps,resources=deployments;statefulsets,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch
// +kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=core,resources=configmaps,verbs=get;list;delete
func (r *PersesReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
start := time.Now()
objKey := req.String()
Expand Down Expand Up @@ -118,7 +120,8 @@ func (r *PersesReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
r.validateVolumes,
r.reconcileProvisioning,
r.reconcileService,
r.reconcileConfigMap,
r.reconcileConfigSecret,
r.cleanupOldConfigMap,
r.reconcileDeployment,
r.reconcileStatefulSet,
r.setStatusToComplete,
Expand Down Expand Up @@ -488,7 +491,7 @@ func (r *PersesReconciler) SetupWithManager(mgr ctrl.Manager) error {
For(&v1alpha2.Perses{}).
Owns(&appsv1.Deployment{}).
Owns(&appsv1.StatefulSet{}).
Owns(&corev1.ConfigMap{}).
Owns(&corev1.Secret{}).
Owns(&corev1.Service{}).
Watches(
&corev1.Secret{},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Copyright The Perses Authors
// Licensed under the Apache License, Version 2.0 (the \"License\");
// 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,
// 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.
Expand All @@ -33,72 +33,71 @@ import (
"github.com/perses/perses-operator/internal/subreconciler"
)

var cmlog = logger.WithField("module", "configmap_controller")
var secretlog = logger.WithField("module", "secret_controller")

func (r *PersesReconciler) reconcileConfigMap(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) {
func (r *PersesReconciler) reconcileConfigSecret(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) {
perses, ok := persesFromContext(ctx)
if !ok {
cmlog.Error("perses not found in context")
secretlog.Error("perses not found in context")
return subreconciler.RequeueWithError(fmt.Errorf("perses not found in context"))
}

configName := common.GetConfigName(perses.Name)

found := &corev1.ConfigMap{}
found := &corev1.Secret{}
if err := r.Get(ctx, types.NamespacedName{Name: configName, Namespace: perses.Namespace}, found); err != nil {
if !apierrors.IsNotFound(err) {
cmlog.WithError(err).Error("Failed to get ConfigMap")
secretlog.WithError(err).Error("Failed to get config Secret")
return subreconciler.RequeueWithError(err)
}

cm, err2 := r.createPersesConfigMap(perses)
sec, err2 := r.createPersesConfigSecret(perses)
if err2 != nil {
cmlog.WithError(err2).Error("Failed to define new ConfigMap resource for perses")
secretlog.WithError(err2).Error("Failed to define new config Secret resource for perses")

meta.SetStatusCondition(&perses.Status.Conditions, metav1.Condition{Type: common.TypeAvailablePerses,
Status: metav1.ConditionFalse, Reason: "Reconciling",
Message: fmt.Sprintf("Failed to create ConfigMap for the custom resource (%s): (%s)", perses.Name, err2)})
Message: fmt.Sprintf("Failed to create config Secret for the custom resource (%s): (%s)", perses.Name, err2)})

if err = r.Status().Update(ctx, perses); err != nil {
cmlog.WithError(err).Error("Failed to update perses status")
secretlog.WithError(err).Error("Failed to update perses status")
return subreconciler.RequeueWithError(err)
}

return subreconciler.RequeueWithError(err2)
}

cmlog.Infof("Creating a new ConfigMap: ConfigMap.Namespace %s ConfigMap.Name %s", cm.Namespace, cm.Name)
if err = r.Create(ctx, cm); err != nil {
cmlog.WithError(err).Errorf("Failed to create new ConfigMap: ConfigMap.Namespace %s ConfigMap.Name %s", cm.Namespace, cm.Name)
secretlog.Infof("Creating a new config Secret: Secret.Namespace %s Secret.Name %s", sec.Namespace, sec.Name)
if err = r.Create(ctx, sec); err != nil {
secretlog.WithError(err).Errorf("Failed to create new config Secret: Secret.Namespace %s Secret.Name %s", sec.Namespace, sec.Name)
return subreconciler.RequeueWithError(err)
}

return subreconciler.ContinueReconciling()
}

cm, err := r.createPersesConfigMap(perses)
sec, err := r.createPersesConfigSecret(perses)
if err != nil {
cmlog.WithError(err).Error("Failed to define new ConfigMap resource for perses")
secretlog.WithError(err).Error("Failed to define new config Secret resource for perses")
return subreconciler.RequeueWithError(err)
}

// call update with dry run to fill out fields that are also returned via the k8s api
if err := r.Update(ctx, cm, client.DryRunAll); err != nil {
cmlog.WithError(err).Error("Failed to update ConfigMap with dry run")
if err := r.Update(ctx, sec, client.DryRunAll); err != nil {
secretlog.WithError(err).Error("Failed to update config Secret with dry run")
return subreconciler.RequeueWithError(err)
}

if configMapNeedsUpdate(found, cm, configName, perses) {
if err := r.Update(ctx, cm); err != nil {
cmlog.WithError(err).Error("Failed to update ConfigMap")
if configSecretNeedsUpdate(found, sec, configName, perses) {
if err := r.Update(ctx, sec); err != nil {
secretlog.WithError(err).Error("Failed to update config Secret")
return subreconciler.RequeueWithError(err)
}
}

return subreconciler.ContinueReconciling()
}

func configMapNeedsUpdate(existing, updated *corev1.ConfigMap, name string, perses *v1alpha2.Perses) bool {
func configSecretNeedsUpdate(existing, updated *corev1.Secret, name string, perses *v1alpha2.Perses) bool {
if existing == nil && updated == nil {
return false
}
Expand All @@ -113,7 +112,6 @@ func configMapNeedsUpdate(existing, updated *corev1.ConfigMap, name string, pers
return true
}

// check for differences only in the labels that are set by the operator
labels := common.LabelsForPerses(name, perses)
for k := range labels {
if existing.Labels[k] != updated.Labels[k] {
Expand All @@ -124,7 +122,7 @@ func configMapNeedsUpdate(existing, updated *corev1.ConfigMap, name string, pers
return false
}

func (r *PersesReconciler) createPersesConfigMap(perses *v1alpha2.Perses) (*corev1.ConfigMap, error) {
func (r *PersesReconciler) createPersesConfigSecret(perses *v1alpha2.Perses) (*corev1.Secret, error) {
configName := common.GetConfigName(perses.Name)
ls := common.LabelsForPerses(configName, perses)

Expand All @@ -134,29 +132,53 @@ func (r *PersesReconciler) createPersesConfigMap(perses *v1alpha2.Perses) (*core
}

persesConfig, err := yaml.Marshal(perses.Spec.Config.Config)

if err != nil {
cmlog.WithError(err).Errorf("Failed to marshal configmap data: ConfigMap.Namespace %s ConfigMap.Name %s", perses.Namespace, configName)
secretlog.WithError(err).Errorf("Failed to marshal config data: Secret.Namespace %s Secret.Name %s", perses.Namespace, configName)
return nil, err
}

configData := map[string]string{
"config.yaml": string(persesConfig),
}

cm := &corev1.ConfigMap{
sec := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: configName,
Namespace: perses.Namespace,
Annotations: annotations,
Labels: ls,
},
Data: configData,
Data: map[string][]byte{
"config.yaml": persesConfig,
},
}

// Set Perses instance as the owner and controller
if err := ctrl.SetControllerReference(perses, cm, r.Scheme); err != nil {
if err := ctrl.SetControllerReference(perses, sec, r.Scheme); err != nil {
return nil, err
}
return cm, nil
return sec, nil
}

// cleanupOldConfigMap deletes any leftover ConfigMap from before the migration
// to Secret-based config storage. This ensures a clean upgrade path.
func (r *PersesReconciler) cleanupOldConfigMap(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) {
perses, ok := persesFromContext(ctx)
if !ok {
secretlog.Error("perses not found in context")
return subreconciler.RequeueWithError(fmt.Errorf("perses not found in context"))
}

configName := common.GetConfigName(perses.Name)
oldCM := &corev1.ConfigMap{}
if err := r.Get(ctx, types.NamespacedName{Name: configName, Namespace: perses.Namespace}, oldCM); err != nil {
if apierrors.IsNotFound(err) {
return subreconciler.ContinueReconciling()
}
secretlog.WithError(err).Error("Failed to check for old ConfigMap")
return subreconciler.RequeueWithError(err)
}

secretlog.Infof("Cleaning up old ConfigMap: %s/%s (migrated to Secret)", oldCM.Namespace, oldCM.Name)
if err := r.Delete(ctx, oldCM); err != nil && !apierrors.IsNotFound(err) {
secretlog.WithError(err).Error("Failed to delete old ConfigMap")
return subreconciler.RequeueWithError(err)
}

return subreconciler.ContinueReconciling()
}
Loading
Loading