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

Watching for Kyma Custom Resource #7

Merged
merged 32 commits into from
Jun 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
a96042e
Watching resource Kyma CR, temp image name
mvshao May 5, 2023
513f358
First version of event filters
mvshao May 8, 2023
a8fcabb
Working watching Kubeconfig field
mvshao May 9, 2023
bc05c0b
Prepare to present two types of watching resources
mvshao May 10, 2023
28c5a68
Fix invalid format in fmt.Sprintf
mvshao May 10, 2023
2d328d5
Code cleanup
mvshao May 10, 2023
5422866
First round of unit tests for controller
mvshao May 15, 2023
3cdb836
Working test cluster, need to implement tests
mvshao May 15, 2023
1eefe3a
Envtests for reconcilation loop
mvshao May 19, 2023
d886495
Fix Makefile
mvshao May 19, 2023
c8669ad
Fix linting errors
mvshao May 22, 2023
b2b479e
Change the default Docker image path
mvshao May 22, 2023
bf79026
Fix imports
mvshao May 22, 2023
bb97a69
Labeling Kyma CR with compass-id
mvshao May 29, 2023
8196d8c
Skip reconcilation if not signicifant filed in CR is chaned
mvshao May 29, 2023
ace9b9a
Pass the custom interface
mvshao May 29, 2023
f0f3c0a
Provide tests for reconcilation loop
mvshao May 30, 2023
0ecc24c
Resolve vulnerability CVE-2023-2253
mvshao May 30, 2023
072ab87
Fix major lint errors
mvshao May 31, 2023
9e5b6bf
Implement BDD Ginkgo tests
mvshao Jun 1, 2023
eab88b4
Fix Sprintf error
mvshao Jun 1, 2023
7fa9906
Proposed refactoring in the controller.
akgalwas Jun 6, 2023
ffdc016
Proposed refactoring in the controller.
akgalwas Jun 6, 2023
84f7ab2
Fix ginkgo tests
mvshao Jun 7, 2023
2d56740
Deterministic env tests
mvshao Jun 12, 2023
f23343f
Reconciler returns errror with requeue
mvshao Jun 12, 2023
947a45a
Proposed refactoring in the controller's tests.
akgalwas Jun 16, 2023
e901238
Ginkgo test refactor and cleanup
mvshao Jun 20, 2023
e7919c3
Describe table for test cases
mvshao Jun 20, 2023
d244c4d
Remove compassManager CRD
mvshao Jun 20, 2023
b436cad
Cleanup in test and in CRD's
mvshao Jun 20, 2023
623034c
Proposal for simplify predicate passing
mvshao Jun 21, 2023
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o ma
# Refer to https://github.com/GoogleContainerTools/distroless for more details
FROM gcr.io/distroless/static:nonroot
WORKDIR /
COPY --from=builder /workspace/manager .
COPY --chown=65532:65532 --from=builder /workspace/manager .
USER 65532:65532

ENTRYPOINT ["/manager"]
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ vet: ## Run go vet against code.
.PHONY: test
test: manifests generate fmt vet envtest ## Run tests.
ifdef COVERFILE
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -coverprofile cover.out
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -v -coverprofile cover.out
else
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -cover
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -v -cover
endif

##@ Build
Expand Down
12 changes: 11 additions & 1 deletion PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,19 @@
# This file is used to track the info used to scaffold your project
# and allow the plugins properly work.
# More info: https://book.kubebuilder.io/reference/project-config.html
domain: compass-manager.kyma-project.io
domain: kyma-project.io
layout:
- go.kubebuilder.io/v3
projectName: compass-manager
repo: github.com/kyma-project/compass-manager
resources:
- api:
crdVersion: v1
namespaced: true
controller: true
domain: kyma-project.io
group: operator
kind: CompassManager
path: github.com/kyma-project/compass-manager/api/v1beta1
version: v1beta1
version: "3"
Empty file removed api/.gitkeep
Empty file.
1 change: 0 additions & 1 deletion config/default/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ commonLabels:
app.kubernetes.io/component: compass-manager.kyma-project.io

bases:
- ../crd
- ../rbac
- ../manager
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
Expand Down
6 changes: 6 additions & 0 deletions config/manager/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
resources:
- manager.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
images:
- name: controller
newName: mvshao/compass-manager
newTag: latest
31 changes: 31 additions & 0 deletions config/rbac/compassmanager_editor_role.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# permissions for end users to edit compassmanagers.
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it is not needed

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: clusterrole
app.kubernetes.io/instance: compassmanager-editor-role
app.kubernetes.io/component: rbac
app.kubernetes.io/created-by: compass-manager
app.kubernetes.io/part-of: compass-manager
app.kubernetes.io/managed-by: kustomize
name: compassmanager-editor-role
rules:
- apiGroups:
- operator.kyma-project.io
resources:
- compassmanagers
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- operator.kyma-project.io
resources:
- compassmanagers/status
verbs:
- get
27 changes: 27 additions & 0 deletions config/rbac/compassmanager_viewer_role.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# permissions for end users to view compassmanagers.
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it needed?

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: clusterrole
app.kubernetes.io/instance: compassmanager-viewer-role
app.kubernetes.io/component: rbac
app.kubernetes.io/created-by: compass-manager
app.kubernetes.io/part-of: compass-manager
app.kubernetes.io/managed-by: kustomize
name: compassmanager-viewer-role
rules:
- apiGroups:
- operator.kyma-project.io
resources:
- compassmanagers
verbs:
- get
- list
- watch
- apiGroups:
- operator.kyma-project.io
resources:
- compassmanagers/status
verbs:
- get
38 changes: 38 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it needed?

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: manager-role
rules:
- apiGroups:
- apiextensions.k8s.io
resources:
- customresourcedefinitions
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- operator.kyma-project.io
resources:
- kymas
verbs:
- get
- list
- update
- watch
- apiGroups:
- operator.kyma-project.io
resources:
- kymas/status
verbs:
- get
12 changes: 12 additions & 0 deletions config/samples/operator_v1beta1_compassmanager.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: operator.kyma-project.io/v1beta1
Copy link
Contributor

Choose a reason for hiding this comment

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

Not needed, as Compass Manager doesn't need have its own CRD.

kind: CompassManager
metadata:
labels:
app.kubernetes.io/name: compassmanager
app.kubernetes.io/instance: compassmanager-sample
app.kubernetes.io/part-of: compass-manager
app.kubernetes.io/managed-by: kustomize
app.kubernetes.io/created-by: compass-manager
name: compassmanager-sample
spec:
# TODO(user): Add fields here
Empty file removed controllers/.gitkeep
Empty file.
182 changes: 182 additions & 0 deletions controllers/compassmanager_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package controllers

import (
"context"
"time"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"

kyma "github.com/kyma-project/lifecycle-manager/api/v1beta1"
log "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/predicate"
)

const (
KymaNameLabel = "operator.kyma-project.io/kyma-name"
CompassIDLabel = "operator.kyma-project.io/compass-id"
// KubeconfigKey is the name of the key in the secret storing cluster credentials.
// The secret is created by KEB: https://github.com/kyma-project/control-plane/blob/main/components/kyma-environment-broker/internal/process/steps/lifecycle_manager_kubeconfig.go
KubeconfigKey = "config"
)

//+kubebuilder:rbac:groups=apiextensions.k8s.io,resources=customresourcedefinitions,verbs=get;list;watch
//+kubebuilder:rbac:groups=operator.kyma-project.io,resources=kymas,verbs=get;list;watch;update
//+kubebuilder:rbac:groups=operator.kyma-project.io,resources=kymas/status,verbs=get
//+kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch

//go:generate mockery --name=Registrator
type Registrator interface {
akgalwas marked this conversation as resolved.
Show resolved Hide resolved
// Register creates Runtime in the Compass system. It must be idempotent.
Register(name string) (string, error)
// ConfigureRuntimeAgent creates a config map in the Runtime that is used by the Compass Runtime Agent. It must be idempotent.
ConfigureRuntimeAgent(kubeconfig string, runtimeID string) error
}

type Client interface {
Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error
Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error
List(ctx context.Context, obj client.ObjectList, opts ...client.ListOption) error
}

// CompassManagerReconciler reconciles a CompassManager object
type CompassManagerReconciler struct {
Client Client
Scheme *runtime.Scheme
Log *log.Logger
Registrator Registrator
}

func NewCompassManagerReconciler(mgr manager.Manager, log *log.Logger, r Registrator) *CompassManagerReconciler {
return &CompassManagerReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: log,
Registrator: r,
}
}

var requeueTime = time.Minute * 5

func (cm *CompassManagerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {

cm.Log.Infof("Reconciliation triggered for Kyma Resource %s", req.Name)
kubeconfig, err := cm.getKubeconfig(req.Name)
if err != nil {
return ctrl.Result{}, err
}

if kubeconfig == "" {
cm.Log.Infof("Kubeconfig for Kyma resource %s not available.", req.Name)
return ctrl.Result{RequeueAfter: requeueTime}, nil
akgalwas marked this conversation as resolved.
Show resolved Hide resolved
}

compassRuntimeID, err := cm.Registrator.Register(req.Name)
if err != nil {
cm.Log.Warnf("Failed to register Runtime for Kyma resource %s: %v.", req.Name, err)
return ctrl.Result{RequeueAfter: requeueTime}, err
}
cm.Log.Infof("Runtime %s registered for Kyma resource %s.", compassRuntimeID, req.Name)

err = cm.Registrator.ConfigureRuntimeAgent(kubeconfig, compassRuntimeID)
if err != nil {
cm.Log.Warnf("Failed to configure Compass Runtime Agent for Kyma resource %s: %v.", req.Name, err)
return ctrl.Result{RequeueAfter: requeueTime}, err
}
cm.Log.Infof("Compass Runtime Agent for Runtime %s configured.", compassRuntimeID)

return ctrl.Result{}, cm.markRuntimeRegistered(req.NamespacedName, compassRuntimeID)
}

func (cm *CompassManagerReconciler) getKubeconfig(kymaName string) (string, error) {
secretList := &corev1.SecretList{}
labelSelector := labels.SelectorFromSet(map[string]string{
KymaNameLabel: kymaName,
})

err := cm.Client.List(context.Background(), secretList, &client.ListOptions{
LabelSelector: labelSelector,
})

if err != nil {
return "", err
}

if len(secretList.Items) == 0 {
return "", nil
}
secret := &secretList.Items[0]

return string(secret.Data[KubeconfigKey]), nil
}

func (cm *CompassManagerReconciler) markRuntimeRegistered(objKey types.NamespacedName, compassID string) error {

instance := &kyma.Kyma{}
err := cm.Client.Get(context.Background(), objKey, instance)
if err != nil {
return err
}

l := instance.GetLabels()
if l == nil {
l = make(map[string]string)
}

l[CompassIDLabel] = compassID

instance.SetLabels(l)

return cm.Client.Update(context.Background(), instance)
}

// SetupWithManager sets up the controller with the Manager.
func (cm *CompassManagerReconciler) SetupWithManager(mgr ctrl.Manager) error {
fieldSelectorPredicate := predicate.Funcs{
akgalwas marked this conversation as resolved.
Show resolved Hide resolved
CreateFunc: func(e event.CreateEvent) bool {
return cm.needsToBeReconciled(e.Object)
},
UpdateFunc: func(e event.UpdateEvent) bool {
return cm.needsToBeReconciled(e.ObjectNew)
},
}

omitStatusChanged := predicate.Or(
predicate.GenerationChangedPredicate{},
predicate.LabelChangedPredicate{},
predicate.AnnotationChangedPredicate{},
)

// We can simplify passing the predicate filters to controller
// The predicates passed in For(builder.WithPredicates()) function is merged with runner.WithEventFilter() predicates to single slice with predicates.
// Proposal: delete the predicates from For() functions, and return runner.WithEventFilter(fieldSelectorPredicate).WithEventFilter(predicates).Complete(cm)

runner := ctrl.NewControllerManagedBy(mgr).
For(&kyma.Kyma{}, builder.WithPredicates(
predicate.And(
predicate.ResourceVersionChangedPredicate{},
omitStatusChanged,
)))

return runner.WithEventFilter(fieldSelectorPredicate).Complete(cm)
}

func (cm *CompassManagerReconciler) needsToBeReconciled(obj runtime.Object) bool {

kymaObj, ok := obj.(*kyma.Kyma)

if !ok {
cm.Log.Error("Unexpected type detected. Object type is supposed to be of Kyma type.")
return false
}
_, labelFound := kymaObj.GetLabels()[CompassIDLabel]

return !labelFound
}
Loading