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

feat: Module release meta watch #1968

Merged
merged 52 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
efab76e
Implement new watch mechanism for ModuleReleaseMeta CRs
jeremyharisch Oct 16, 2024
0b930ea
Merge branch 'main' into moduleReleaseMetaWatch
jeremyharisch Oct 16, 2024
4add134
Setup new test
jeremyharisch Oct 17, 2024
9bcded9
Replace requeue times for test
jeremyharisch Oct 18, 2024
200366b
Test commit
jeremyharisch Oct 18, 2024
f9f78a2
Remove unneeded change
jeremyharisch Oct 18, 2024
cc7759d
Check if Kyma gets requeud and into Error State
jeremyharisch Oct 18, 2024
292a4d9
Fix linting
jeremyharisch Oct 18, 2024
046938b
Fix reque mechanism
jeremyharisch Oct 23, 2024
a4474c9
remove unneeded files, they will be generated automatically
jeremyharisch Oct 24, 2024
32df1b2
Check watcher startup before teststart
jeremyharisch Oct 25, 2024
adf5811
Merge branch 'main' into moduleReleaseMetaWatch
jeremyharisch Oct 25, 2024
3a898fc
Deploy template-operator incl ModuleReleaseMeta
jeremyharisch Oct 26, 2024
25e9920
Test commit (will be reverted)
jeremyharisch Oct 28, 2024
b2877d6
remove consistently for tryout
jeremyharisch Oct 28, 2024
51d37c2
Merge branch 'main' into moduleReleaseMetaWatch
jeremyharisch Oct 28, 2024
9c5f1cf
More Ready checks
jeremyharisch Oct 28, 2024
437eb44
Merge branch 'moduleReleaseMetaWatch' of github.com:jeremyharisch/lif…
jeremyharisch Oct 28, 2024
c8ebcbb
Check module Default CR exists
jeremyharisch Oct 28, 2024
8fd16ae
gofmpted
jeremyharisch Oct 28, 2024
405f427
fix linting
jeremyharisch Oct 28, 2024
8fd597a
gci
jeremyharisch Oct 28, 2024
cfce9d0
Refactor implementation to use TypedEventHandlerInterface
jeremyharisch Oct 29, 2024
0868dc6
Merge branch 'main' into moduleReleaseMetaWatch
jeremyharisch Oct 29, 2024
9329d78
Simplify functions
jeremyharisch Oct 29, 2024
b6abdfc
Merge branch 'main' into moduleReleaseMetaWatch
jeremyharisch Oct 29, 2024
53e2a54
Fix linting
jeremyharisch Oct 29, 2024
236ee6f
Merge branch 'moduleReleaseMetaWatch' of github.com:jeremyharisch/lif…
jeremyharisch Oct 29, 2024
35d806f
Merge branch 'main' into moduleReleaseMetaWatch
jeremyharisch Oct 29, 2024
d9b63ab
Merge branch 'main' into moduleReleaseMetaWatch
jeremyharisch Oct 29, 2024
e12684a
Revert some vs code formatting
jeremyharisch Oct 29, 2024
fb444cb
Merge branch 'moduleReleaseMetaWatch' of github.com:jeremyharisch/lif…
jeremyharisch Oct 29, 2024
cd8acc7
revert vscode changes
jeremyharisch Oct 29, 2024
c204225
Merge branch 'main' into moduleReleaseMetaWatch
jeremyharisch Oct 29, 2024
532bf36
Revert vs Code changes
jeremyharisch Oct 29, 2024
8ef5d2a
Merge branch 'moduleReleaseMetaWatch' of github.com:jeremyharisch/lif…
jeremyharisch Oct 29, 2024
12ae794
revert vs code changes
jeremyharisch Oct 29, 2024
f1f3c06
Merge branch 'main' into moduleReleaseMetaWatch
jeremyharisch Oct 30, 2024
138dad8
formatting
jeremyharisch Oct 30, 2024
ee3a9d9
Merge branch 'moduleReleaseMetaWatch' of github.com:jeremyharisch/lif…
jeremyharisch Oct 30, 2024
3f34262
Init Change Handler
jeremyharisch Oct 30, 2024
7931483
Implement Review comments
jeremyharisch Oct 30, 2024
bac972e
Add more test cases
jeremyharisch Oct 30, 2024
c24e5b8
Merge branch 'main' into moduleReleaseMetaWatch
jeremyharisch Oct 30, 2024
5beb217
Fix pkg import
jeremyharisch Oct 30, 2024
687f489
Fix deadlink
jeremyharisch Oct 30, 2024
23a8ad6
fix deadlink
jeremyharisch Oct 30, 2024
9ee33c1
Fix typo
jeremyharisch Oct 30, 2024
dcd61ec
Include more details module checks
jeremyharisch Oct 30, 2024
3c34327
Fix comma mistake
jeremyharisch Oct 30, 2024
eb8cf27
gofumpt
jeremyharisch Oct 30, 2024
d8fda47
Apply suggestions from code review
jeremyharisch Oct 30, 2024
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
27 changes: 24 additions & 3 deletions .github/actions/deploy-lifecycle-manager-e2e/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ runs:
kustomize edit add patch --path purge_finalizer.yaml --kind Deployment
popd
- name: Patch metrics cleanup interval
if : ${{ matrix.e2e-test == 'kyma-metrics' }}
if: ${{ matrix.e2e-test == 'kyma-metrics' }}
working-directory: lifecycle-manager
shell: bash
run: |
Expand Down Expand Up @@ -58,6 +58,28 @@ runs:
cat self-signed-cert.yaml
kustomize edit add patch --path self-signed-cert.yaml --kind Deployment
popd
- name: Patch requeue intervals
if: ${{ matrix.e2e-test == 'modulereleasemeta-watch-trigger'}}
working-directory: lifecycle-manager
shell: bash
run: |
pushd config/watcher_local_test
echo \
"- op: replace
path: /spec/template/spec/containers/0/args/16
value: --kyma-requeue-success-interval=1h
- op: add
path: /spec/template/spec/containers/0/args/-
value: --kyma-requeue-warning-interval=1h
- op: add
path: /spec/template/spec/containers/0/args/-
value: --kyma-requeue-error-interval=1h
- op: add
path: /spec/template/spec/containers/0/args/-
value: --kyma-requeue-busy-interval=1s" >> requeue-interval-patch.yaml
cat requeue-interval-patch.yaml
kustomize edit add patch --path requeue-interval-patch.yaml --kind Deployment
popd
- name: Patch CA certificate renewBefore
if: ${{matrix.e2e-test == 'ca-certificate-rotation'}}
working-directory: lifecycle-manager
Expand All @@ -84,8 +106,7 @@ runs:
if: ${{ matrix.e2e-test == 'kyma-metrics' ||
matrix.e2e-test == 'purge-metrics' ||
matrix.e2e-test == 'self-signed-certificate-rotation' ||
matrix.e2e-test == 'mandatory-module-metrics'
}}
matrix.e2e-test == 'mandatory-module-metrics'}}
shell: bash
run: |
kubectl patch svc klm-controller-manager-metrics -p '{"spec": {"type": "LoadBalancer"}}' -n kcp-system
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ runs:
matrix.e2e-test == 'module-consistency' ||
matrix.e2e-test == 'skip-manifest-reconciliation' ||
matrix.e2e-test == 'misconfigured-kyma-secret' ||
matrix.e2e-test == 'unmanage-module'
matrix.e2e-test == 'unmanage-module' ||
matrix.e2e-test == 'modulereleasemeta-watch-trigger'
}}
shell: bash
run: |
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/test-e2e-with-modulereleasemeta.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
description: With Kubernetes version
required: false
pull_request:
types: [ opened, edited, synchronize, reopened, ready_for_review ]
types: [opened, edited, synchronize, reopened, ready_for_review]
jobs:
wait-for-image-build:
name: Wait for image build
Expand Down Expand Up @@ -57,6 +57,8 @@ jobs:
- ocm-compatible-module-template
- modulereleasemeta-with-obsolete-moduletemplate
- modulereleasemeta-sync
- modulereleasemeta-watch-trigger

runs-on: ubuntu-latest
timeout-minutes: 20
steps:
Expand Down
1 change: 1 addition & 0 deletions config/rbac/common/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ rules:
verbs:
- get
- list
- watch
- apiGroups:
- operator.kyma-project.io
resources:
Expand Down
6 changes: 6 additions & 0 deletions docs/contributor/02-controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ If a ModuleReleaseMeta CR for a particular module doesn't exist, Kyma Controller

The Kyma CR in Kyma Control Plane shows the initial specification and the current status. To install a module, Lifecycle Manager uses the specification from the remote cluster Kyma CR.

### Requeuing the Kyma CR

The `Kyma` CR is requeued at set intervals using specific flags from Lifecycle Manager. The requeuing ensures that the Kyma CR is periodically reprocessed, allowing the controller to detect and apply any changes that may have occurred during that time. Additionally, several watch mechanisms are implemented, enabling the controller to requeue Kyma CRs when certain events occur.

These watch mechanisms monitor Kyma, Secret, Manifest, and ModuleReleaseMeta CRs, ensuring that the relevant Kyma CRs are requeued whenever these CRs are created, updated, or deleted. Additionally, the watch mechanism for ModuleReleaseMeta CRs has a [dedicated implementation](../../internal/watch/modulereleasemeta_change.go), which ensures that all Kyma CRs using a module in a channel affected by the ModuleReleaseMeta CR are requeued as needed.

## Mandatory Modules Controllers

Lifecycle Manager uses two Mandatory Modules Controllers:
Expand Down
5 changes: 3 additions & 2 deletions docs/contributor/04-local-test-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ This setup is deployed with the following security features enabled:
```shell
k3d cluster create kcp-local --port 9443:443@loadbalancer \
--registry-create k3d-registry.localhost:0.0.0.0:5111 \
--k3s-arg '--disable=traefik@server:0'
--k3s-arg '--disable=traefik@server:0' \
--k3s-arg --tls-san=host.k3d.internal@server:*
```

2. Open `/etc/hosts` file on your local system:
Expand Down Expand Up @@ -148,7 +149,7 @@ This setup is deployed with the following security features enabled:
Create a local Kyma runtime (SKR) cluster:

```shell
k3d cluster create skr-local
k3d cluster create skr-local --k3s-arg --tls-san=host.k3d.internal@server:*
```

### Create a Kyma CR and a Remote Secret
Expand Down
2 changes: 1 addition & 1 deletion internal/controller/kyma/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ type Reconciler struct {
// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch
// +kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=operator.kyma-project.io,resources=moduletemplates,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=operator.kyma-project.io,resources=modulereleasemetas,verbs=get;list
// +kubebuilder:rbac:groups=operator.kyma-project.io,resources=modulereleasemetas,verbs=get;list;watch
// +kubebuilder:rbac:groups=operator.kyma-project.io,resources=moduletemplates/finalizers,verbs=update
// +kubebuilder:rbac:groups=apiextensions.k8s.io,resources=customresourcedefinitions,verbs=get;list;watch
// +kubebuilder:rbac:groups=cert-manager.io,resources=issuers,verbs=get;list;watch
Expand Down
3 changes: 2 additions & 1 deletion internal/controller/kyma/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import (

"github.com/kyma-project/lifecycle-manager/api/shared"
"github.com/kyma-project/lifecycle-manager/api/v1beta2"
"github.com/kyma-project/lifecycle-manager/internal/watch"
"github.com/kyma-project/lifecycle-manager/pkg/security"
"github.com/kyma-project/lifecycle-manager/pkg/watch"
)

type SetupOptions struct {
Expand Down Expand Up @@ -63,6 +63,7 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager, opts ctrlruntime.Options
WithEventFilter(predicate.Or(predicate.GenerationChangedPredicate{}, predicate.LabelChangedPredicate{})).
Watches(&v1beta2.ModuleTemplate{},
handler.EnqueueRequestsFromMapFunc(watch.NewTemplateChangeHandler(r).Watch())).
Watches(&v1beta2.ModuleReleaseMeta{}, watch.NewModuleReleaseMetaEventHandler(r)).
Watches(&apicorev1.Secret{}, handler.Funcs{}).
Watches(&v1beta2.Manifest{},
handler.EnqueueRequestForOwner(mgr.GetScheme(), mgr.GetRESTMapper(), &v1beta2.Kyma{},
Expand Down
2 changes: 1 addition & 1 deletion internal/controller/mandatorymodule/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/predicate"

"github.com/kyma-project/lifecycle-manager/api/v1beta2"
"github.com/kyma-project/lifecycle-manager/pkg/watch"
"github.com/kyma-project/lifecycle-manager/internal/watch"
)

const (
Expand Down
182 changes: 182 additions & 0 deletions internal/watch/modulereleasemeta_change.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package watch

import (
"context"

"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/workqueue"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

"github.com/kyma-project/lifecycle-manager/api/v1beta2"
)

type ModuleReleaseMetaEventHandler = TypedModuleReleaseMetaEventHandler[client.Object, reconcile.Request]

// TypedModuleReleaseMetaEventHandler implements handler.EventHandler.
type TypedModuleReleaseMetaEventHandler[object any, request comparable] struct {
client.Reader

CreateFunc func(context.Context, event.TypedCreateEvent[object], workqueue.TypedRateLimitingInterface[request])

UpdateFunc func(context.Context, event.TypedUpdateEvent[object], workqueue.TypedRateLimitingInterface[request])

DeleteFunc func(context.Context, event.TypedDeleteEvent[object], workqueue.TypedRateLimitingInterface[request])

GenericFunc func(context.Context, event.TypedGenericEvent[object], workqueue.TypedRateLimitingInterface[request])
}

func NewModuleReleaseMetaEventHandler(handlerClient ChangeHandlerClient) *ModuleReleaseMetaEventHandler {
return &ModuleReleaseMetaEventHandler{Reader: handlerClient}
}

// Create handles Create events.
func (m TypedModuleReleaseMetaEventHandler[object, request]) Create(ctx context.Context, event event.CreateEvent,
rli workqueue.TypedRateLimitingInterface[reconcile.Request],
) {
handleEvent(ctx, event, rli, m.Reader)
}

// Delete handles Delete events.
func (m TypedModuleReleaseMetaEventHandler[object, request]) Delete(ctx context.Context, event event.DeleteEvent,
rli workqueue.TypedRateLimitingInterface[reconcile.Request],
) {
handleEvent(ctx, event, rli, m.Reader)
}

// Generic handles generic events.
func (m TypedModuleReleaseMetaEventHandler[object, request]) Generic(ctx context.Context, event event.GenericEvent,
rli workqueue.TypedRateLimitingInterface[reconcile.Request],
) {
handleEvent(ctx, event, rli, m.Reader)
}

// Update handles Update events.
func (m TypedModuleReleaseMetaEventHandler[object, request]) Update(ctx context.Context, event event.UpdateEvent,
rli workqueue.TypedRateLimitingInterface[reconcile.Request],
) {
kymaList, err := getKymaList(ctx, m.Reader)
if err != nil {
return
}

oldModuleReleaseMeta, ok := event.ObjectOld.(*v1beta2.ModuleReleaseMeta)
if !ok {
return
}
newModuleReleaseMeta, ok := event.ObjectNew.(*v1beta2.ModuleReleaseMeta)
if !ok {
return
}
diff := DiffModuleReleaseMetaChannels(oldModuleReleaseMeta, newModuleReleaseMeta)

affectedKymas := GetAffectedKymas(kymaList, newModuleReleaseMeta.Spec.ModuleName, diff)

requeueKymas(rli, affectedKymas)
}

// DiffModuleReleaseMetaChannels determines the difference between the old and new ModuleReleaseMeta channels. It returns
// a map of the channels that have been updated or added.
func DiffModuleReleaseMetaChannels(oldModuleReleaseMeta, newModuleReleaseMeta *v1beta2.ModuleReleaseMeta) map[string]v1beta2.ChannelVersionAssignment {
diff := make(map[string]v1beta2.ChannelVersionAssignment)

oldChannels := make(map[string]v1beta2.ChannelVersionAssignment)
for _, channel := range oldModuleReleaseMeta.Spec.Channels {
oldChannels[channel.Channel] = channel
}

newChannels := make(map[string]v1beta2.ChannelVersionAssignment)
for _, newChannel := range newModuleReleaseMeta.Spec.Channels {
newChannels[newChannel.Channel] = newChannel
oldChannel, ok := oldChannels[newChannel.Channel]
if !ok || oldChannel.Version != newChannel.Version {
diff[newChannel.Channel] = newChannel
}
}

for oldChannelName, oldChannel := range oldChannels {
if _, ok := newChannels[oldChannelName]; !ok {
diff[oldChannelName] = oldChannel
}
}

return diff
}

// GetAffectedKymas determines which Kymas are affected by the update. It returns a list of Kymas that have modules
// assigned to the updated channels.
func GetAffectedKymas(kymas *v1beta2.KymaList, moduleName string,
newChannelAssignments map[string]v1beta2.ChannelVersionAssignment,
) []*types.NamespacedName {
affectedKymas := make([]*types.NamespacedName, 0)
for _, kyma := range kymas.Items {
for _, module := range kyma.Status.Modules {
if module.Name != moduleName {
continue
}
moduleChannel := module.Channel

if moduleChannel == "" {
moduleChannel = kyma.Spec.Channel
}

newAssignment, ok := newChannelAssignments[moduleChannel]

if ok && (moduleChannel == newAssignment.Channel) {
affectedKymas = append(affectedKymas,
&types.NamespacedName{Name: kyma.GetName(), Namespace: kyma.GetNamespace()})
break
}
}
}
return affectedKymas
}

func handleEvent(ctx context.Context, evt interface{}, rli workqueue.TypedRateLimitingInterface[reconcile.Request], reader client.Reader) {
kymaList, err := getKymaList(ctx, reader)
if err != nil {
return
}

var moduleReleaseMeta *v1beta2.ModuleReleaseMeta
var ok bool
switch eventInstance := evt.(type) {
case event.CreateEvent:
moduleReleaseMeta, ok = eventInstance.Object.(*v1beta2.ModuleReleaseMeta)
case event.DeleteEvent:
moduleReleaseMeta, ok = eventInstance.Object.(*v1beta2.ModuleReleaseMeta)
case event.GenericEvent:
moduleReleaseMeta, ok = eventInstance.Object.(*v1beta2.ModuleReleaseMeta)
default:
return
}

if !ok {
return
}

channelAssignment := getChannelAssignmentMapping(moduleReleaseMeta)
affectedKymas := GetAffectedKymas(kymaList, moduleReleaseMeta.Spec.ModuleName, channelAssignment)

requeueKymas(rli, affectedKymas)
}

func requeueKymas(rli workqueue.TypedRateLimitingInterface[reconcile.Request], kymas []*types.NamespacedName) {
for _, kyma := range kymas {
rli.Add(reconcile.Request{
NamespacedName: types.NamespacedName{
Name: kyma.Name,
Namespace: kyma.Namespace,
},
})
}
}

func getChannelAssignmentMapping(moduleReleaseMeta *v1beta2.ModuleReleaseMeta) map[string]v1beta2.ChannelVersionAssignment {
channelMapping := make(map[string]v1beta2.ChannelVersionAssignment)
for _, channelAssignment := range moduleReleaseMeta.Spec.Channels {
channelMapping[channelAssignment.Channel] = channelAssignment
}
return channelMapping
}
Loading
Loading