Skip to content
This repository has been archived by the owner on Aug 19, 2024. It is now read-only.

Commit

Permalink
Recreate Backstage Pod on changes of ConfigMaps/Secrets configured in…
Browse files Browse the repository at this point in the history
… CR (#351)

* watch and reconcile config

Signed-off-by: gazarenkov <[email protected]>

* example

Signed-off-by: gazarenkov <[email protected]>

* recreate by secret

Signed-off-by: gazarenkov <[email protected]>

* add auto-recreate labels/annotations by operator by default and configure

Signed-off-by: gazarenkov <[email protected]>

* Regenerate bundle manifests

Co-authored-by: gazarenkov <[email protected]>

* fic lint

Signed-off-by: gazarenkov <[email protected]>

* small fixes

Signed-off-by: gazarenkov <[email protected]>

* add comment

Signed-off-by: gazarenkov <[email protected]>

* fix manifest

Signed-off-by: gazarenkov <[email protected]>

* test preprocessor

Signed-off-by: gazarenkov <[email protected]>

* remove db config from default app-config

Signed-off-by: gazarenkov <[email protected]>

* Regenerate bundle manifests

Co-authored-by: gazarenkov <[email protected]>

* Sync cluster permissions in RHDH CSV with upstream CSV

---------

Signed-off-by: gazarenkov <[email protected]>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: gazarenkov <[email protected]>
Co-authored-by: Armel Soro <[email protected]>
  • Loading branch information
4 people committed May 21, 2024
1 parent c34751e commit 806c5af
Show file tree
Hide file tree
Showing 31 changed files with 1,065 additions and 338 deletions.
21 changes: 1 addition & 20 deletions .rhdh/bundle/manifests/rhdh-operator.csv.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ spec:
- ""
resources:
- configmaps
- secrets
- services
verbs:
- create
Expand All @@ -99,30 +100,10 @@ spec:
- get
- list
- watch
- apiGroups:
- ""
resources:
- secrets
verbs:
- create
- delete
- patch
- update
- apiGroups:
- apps
resources:
- deployments
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- apps
resources:
- statefulsets
verbs:
- create
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ test: manifests generate fmt vet envtest ## Run tests. We need LOCALBIN=$(LOCALB
.PHONY: integration-test
integration-test: ginkgo manifests generate fmt vet envtest ## Run integration_tests. We need LOCALBIN=$(LOCALBIN) to get correct default-config path
mkdir -p $(LOCALBIN)/default-config && cp config/manager/$(CONF_DIR)/* $(LOCALBIN)/default-config
LOCALBIN=$(LOCALBIN) KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" $(GINKGO) -v -r integration_tests
LOCALBIN=$(LOCALBIN) KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" $(GINKGO) -v -r $(ARGS) integration_tests


##@ Build
Expand Down
4 changes: 0 additions & 4 deletions bundle/manifests/backstage-default-config_v1_configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ data:
data:
default.app-config.yaml: |
backend:
database:
connection:
password: ${POSTGRES_PASSWORD}
user: ${POSTGRES_USER}
auth:
keys:
# This is a default value, which you should change by providing your own app-config
Expand Down
23 changes: 2 additions & 21 deletions bundle/manifests/backstage-operator.clusterserviceversion.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ metadata:
}
]
capabilities: Seamless Upgrades
createdAt: "2024-04-01T18:15:06Z"
createdAt: "2024-05-21T10:47:18Z"
operatorframework.io/suggested-namespace: backstage-system
operators.operatorframework.io/builder: operator-sdk-v1.33.0
operators.operatorframework.io/project_layout: go.kubebuilder.io/v3
Expand Down Expand Up @@ -50,6 +50,7 @@ spec:
- ""
resources:
- configmaps
- secrets
- services
verbs:
- create
Expand All @@ -68,30 +69,10 @@ spec:
- get
- list
- watch
- apiGroups:
- ""
resources:
- secrets
verbs:
- create
- delete
- patch
- update
- apiGroups:
- apps
resources:
- deployments
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- apps
resources:
- statefulsets
verbs:
- create
Expand Down
4 changes: 0 additions & 4 deletions config/manager/default-config/app-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ metadata:
data:
default.app-config.yaml: |
backend:
database:
connection:
password: ${POSTGRES_PASSWORD}
user: ${POSTGRES_USER}
auth:
keys:
# This is a default value, which you should change by providing your own app-config
Expand Down
21 changes: 1 addition & 20 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ rules:
- ""
resources:
- configmaps
- secrets
- services
verbs:
- create
Expand All @@ -27,30 +28,10 @@ rules:
- get
- list
- watch
- apiGroups:
- ""
resources:
- secrets
verbs:
- create
- delete
- patch
- update
- apiGroups:
- apps
resources:
- deployments
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- apps
resources:
- statefulsets
verbs:
- create
Expand Down
131 changes: 100 additions & 31 deletions controllers/backstage_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ import (
"fmt"
"reflect"

"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

"k8s.io/apimachinery/pkg/runtime/schema"

"k8s.io/apimachinery/pkg/types"
Expand All @@ -44,7 +50,15 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log"
)

var recNumber = 0
var watchedConfigSelector = metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: model.ExtConfigSyncLabel,
Values: []string{"true"},
Operator: metav1.LabelSelectorOpIn,
},
},
}

// BackstageReconciler reconciles a Backstage object
type BackstageReconciler struct {
Expand All @@ -53,23 +67,16 @@ type BackstageReconciler struct {
// If true, Backstage Controller always sync the state of runtime objects created
// otherwise, runtime objects can be re-configured independently
OwnsRuntime bool

// Namespace allows to restrict the reconciliation to this particular namespace,
// and ignore requests from other namespaces.
// This is mostly useful for our tests, to overcome a limitation of EnvTest about namespace deletion.
Namespace string

// indicates if current cluster is Openshift
IsOpenShift bool
}

//+kubebuilder:rbac:groups=rhdh.redhat.com,resources=backstages,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=rhdh.redhat.com,resources=backstages/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=rhdh.redhat.com,resources=backstages/finalizers,verbs=update
//+kubebuilder:rbac:groups="",resources=configmaps;services,verbs=get;watch;create;update;list;delete;patch
//+kubebuilder:rbac:groups="",resources=configmaps;secrets;services,verbs=get;watch;create;update;list;delete;patch
//+kubebuilder:rbac:groups="",resources=persistentvolumes;persistentvolumeclaims,verbs=get;list;watch
//+kubebuilder:rbac:groups="",resources=secrets,verbs=create;delete;patch;update
//+kubebuilder:rbac:groups="apps",resources=deployments,verbs=get;watch;create;update;list;delete;patch
//+kubebuilder:rbac:groups="apps",resources=statefulsets,verbs=get;watch;create;update;list;delete;patch
//+kubebuilder:rbac:groups="apps",resources=deployments;statefulsets,verbs=get;watch;create;update;list;delete;patch
//+kubebuilder:rbac:groups="route.openshift.io",resources=routes;routes/custom-host,verbs=get;watch;create;update;list;delete;patch

// Reconcile is part of the main kubernetes reconciliation loop which aims to
Expand All @@ -79,16 +86,6 @@ type BackstageReconciler struct {
func (r *BackstageReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
lg := log.FromContext(ctx)

recNumber = recNumber + 1
lg.V(1).Info(fmt.Sprintf("starting reconciliation (namespace: %q), number %d", req.NamespacedName, recNumber))

// Ignore requests for other namespaces, if specified.
// This is mostly useful for our tests, to overcome a limitation of EnvTest about namespace deletion.
// More details on https://book.kubebuilder.io/reference/envtest.html#namespace-usage-limitation
if r.Namespace != "" && req.Namespace != r.Namespace {
return ctrl.Result{}, nil
}

backstage := bs.Backstage{}
if err := r.Get(ctx, req.NamespacedName, &backstage); err != nil {
if errors.IsNotFound(err) {
Expand Down Expand Up @@ -282,18 +279,90 @@ func setStatusCondition(backstage *bs.Backstage, condType bs.BackstageConditionT
})
}

// requestByLabel returns a request with current Namespace and Backstage Object name taken from label
// or empty request object if label not found
func (r *BackstageReconciler) requestByLabel(ctx context.Context, object client.Object) []reconcile.Request {

lg := log.FromContext(ctx)

backstageName := object.GetAnnotations()[model.BackstageNameAnnotation]
if backstageName == "" {
lg.V(1).Info(fmt.Sprintf("warning: %s annotation is not defined for %s, Backstage instances will not be reconciled in this loop", model.BackstageNameAnnotation, object.GetName()))
return []reconcile.Request{}
}

nn := types.NamespacedName{
Namespace: object.GetNamespace(),
Name: backstageName,
}

backstage := bs.Backstage{}
if err := r.Get(ctx, nn, &backstage); err != nil {
if !errors.IsNotFound(err) {
lg.Error(err, "request by label failed, get Backstage ")
}
return []reconcile.Request{}
}

ec, err := r.preprocessSpec(ctx, backstage)
if err != nil {
lg.Error(err, "request by label failed, preprocess Backstage ")
return []reconcile.Request{}
}

deploy := &appsv1.Deployment{}
if err := r.Get(ctx, types.NamespacedName{Name: model.DeploymentName(backstage.Name), Namespace: object.GetNamespace()}, deploy); err != nil {
if errors.IsNotFound(err) {
lg.V(1).Info("request by label, deployment not found", "name", model.DeploymentName(backstage.Name))
} else {
lg.Error(err, "request by label failed, get Deployment ", "error ", err)
}
return []reconcile.Request{}
}

newHash := ec.GetHash()
oldHash := deploy.Spec.Template.ObjectMeta.GetAnnotations()[model.ExtConfigHashAnnotation]
if newHash == oldHash {
lg.V(1).Info("request by label, hash are equal", "hash", newHash)
return []reconcile.Request{}
}

lg.V(1).Info("enqueuing reconcile for", object.GetObjectKind().GroupVersionKind().Kind, object.GetName(), "new hash: ", newHash, "old hash: ", oldHash)
return []reconcile.Request{{NamespacedName: types.NamespacedName{Name: backstage.Name, Namespace: object.GetNamespace()}}}

}

// SetupWithManager sets up the controller with the Manager.
func (r *BackstageReconciler) SetupWithManager(mgr ctrl.Manager) error {

builder := ctrl.NewControllerManagedBy(mgr).
For(&bs.Backstage{})

// [GA] do not remove it
//if r.OwnsRuntime {
// builder.Owns(&appsv1.Deployment{}).
// Owns(&corev1.Service{}).
// Owns(&appsv1.StatefulSet{})
//}
pred, err := predicate.LabelSelectorPredicate(watchedConfigSelector)
if err != nil {
return fmt.Errorf("failed to construct the predicate for matching secrets. This should not happen: %w", err)
}

return builder.Complete(r)
b := ctrl.NewControllerManagedBy(mgr).
For(&bs.Backstage{}).
Watches(
&corev1.Secret{},
handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, o client.Object) []reconcile.Request {
return r.requestByLabel(ctx, o)
}),
builder.WithPredicates(pred, predicate.Funcs{
DeleteFunc: func(e event.DeleteEvent) bool { return true },
UpdateFunc: func(e event.UpdateEvent) bool { return true },
//CreateFunc: func(e event.CreateEvent) bool { return true },
}),
).
Watches(
&corev1.ConfigMap{},
handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, o client.Object) []reconcile.Request {
return r.requestByLabel(ctx, o)
}),
builder.WithPredicates(pred, predicate.Funcs{
DeleteFunc: func(e event.DeleteEvent) bool { return true },
UpdateFunc: func(e event.UpdateEvent) bool { return true },
//CreateFunc: func(e event.CreateEvent) bool { return true },
}))

return b.Complete(r)
}
Loading

0 comments on commit 806c5af

Please sign in to comment.