Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
19b8900
#129 Reconciliation of the blueprint on changes in managed resources
meiserloh Oct 2, 2025
99373b0
Merge remote-tracking branch 'origin/feature/121.3-remove-components'…
meiserloh Oct 2, 2025
8e34e1d
#129 add debounce touch on reconcile start to avoid more unnecessary …
meiserloh Oct 2, 2025
d4404b2
Merge remote-tracking branch 'origin/feature/121.3-remove-components'…
meiserloh Oct 2, 2025
4a16b12
#129 remove debounce touch, because it can omit trailing events that …
meiserloh Oct 2, 2025
ff6d472
#129 handle error on reading debounce window env var
meiserloh Oct 3, 2025
e08ab6f
#131 add debugMode repository and domain object
meiserloh Oct 3, 2025
3c0e88f
#131 ignore logging config when debug mode is active
meiserloh Oct 3, 2025
ac69f8e
#129 fix main tests
meiserloh Oct 3, 2025
eab10e2
Merge remote-tracking branch 'origin/feature/129-reconcile-on-seconda…
meiserloh Oct 3, 2025
abac805
#131 add debug mode read role and rolebinding
meiserloh Oct 3, 2025
4cac091
#131 add phase rollback to isDebugModeActive check
meiserloh Oct 3, 2025
7cb63f2
#131 Handle DebugMode in StateDiff use case
meiserloh Oct 6, 2025
84cd5e6
#129 include global config in config map watches
meiserloh Oct 6, 2025
7c2ddf2
Merge remote-tracking branch 'origin/feature/129-reconcile-on-seconda…
meiserloh Oct 6, 2025
f872244
Merge remote-tracking branch 'origin/feature/121.3-remove-components'…
meiserloh Oct 7, 2025
81b9391
Merge remote-tracking branch 'origin/feature/129-reconcile-on-seconda…
meiserloh Oct 7, 2025
1f77c86
Merge remote-tracking branch 'origin/feature/121.3-remove-components'…
meiserloh Oct 7, 2025
ff00959
Merge remote-tracking branch 'origin/feature/129-reconcile-on-seconda…
meiserloh Oct 7, 2025
fb0f915
#129 replace List-call with ListIds to avoid working on api-objects
meiserloh Oct 7, 2025
f4f0fb7
Merge remote-tracking branch 'origin/feature/129-reconcile-on-seconda…
meiserloh Oct 7, 2025
dbf839c
#131 reset dogu version if not installed (yet)
meiserloh Oct 7, 2025
6a06d82
Merge remote-tracking branch 'origin/feature/121.3-remove-components'…
meiserloh Oct 7, 2025
57547dd
Merge remote-tracking branch 'origin/feature/129-reconcile-on-seconda…
meiserloh Oct 7, 2025
22b0d52
Merge remote-tracking branch 'origin/feature/121.3-remove-components'…
meiserloh Oct 8, 2025
95b895a
Merge remote-tracking branch 'origin/feature/129-reconcile-on-seconda…
meiserloh Oct 8, 2025
3f347d9
Merge remote-tracking branch 'origin/feature/121.3-remove-components'…
meiserloh Oct 8, 2025
57670c9
Merge remote-tracking branch 'origin/feature/129-reconcile-on-seconda…
meiserloh Oct 8, 2025
c4ba126
#131 add changelog
meiserloh Oct 10, 2025
106b8e5
Merge remote-tracking branch 'origin/feature/121.3-remove-components'…
meiserloh Oct 10, 2025
ab42be8
Merge remote-tracking branch 'origin/feature/129-reconcile-on-seconda…
meiserloh Oct 10, 2025
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: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
- [#121] Added use case to check if dogus actually use the desired version and config before completing the blueprint
- [#129] Reconciliation of the blueprint on changes of dogu-crs, ces-configMaps and ces-secrets
- [#131] Ignore loglevel changes while debug-mode is active

### Changed
- [#119] *breaking* sensitive dogu config can now only be referenced with secrets
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/cloudogu/ces-commons-lib v0.2.0
github.com/cloudogu/cesapp-lib v0.18.1
github.com/cloudogu/k8s-blueprint-lib/v2 v2.0.0-20250925091741-275e54da827b
github.com/cloudogu/k8s-debug-mode-cr-lib v1.0.0
github.com/cloudogu/k8s-dogu-lib/v2 v2.0.0-20250924122244-01339f784cd0
github.com/cloudogu/k8s-registry-lib v0.2.2-0.20250818112109-cfd93e57e9f6
github.com/cloudogu/remote-dogu-descriptor-lib v0.1.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ github.com/cloudogu/cesapp-lib v0.18.1 h1:LMdGktIefm/PuhdPqpLTPvjY1smO06EEGBbRSA
github.com/cloudogu/cesapp-lib v0.18.1/go.mod h1:J05eXFxnz4enZblABlmiVTZaUtJ+LIhlJ2UF6l9jpDw=
github.com/cloudogu/k8s-blueprint-lib/v2 v2.0.0-20250925091741-275e54da827b h1:1VRisF6h7o/B8fOtPIRuMApMe4PQKiCgiYDLD2tyScw=
github.com/cloudogu/k8s-blueprint-lib/v2 v2.0.0-20250925091741-275e54da827b/go.mod h1:yNtYKIfJkJyMCQ5srtWspl4CDIu3pCvfNC4Yci1XVtQ=
github.com/cloudogu/k8s-debug-mode-cr-lib v1.0.0 h1:geZjXwWQY8d8aEWA9l2is/DwlADdOHQveBokPK63XH0=
github.com/cloudogu/k8s-debug-mode-cr-lib v1.0.0/go.mod h1:OPAO5P5ZSZkEexP9YOWNj4wEE8T3wqs92yyP5muymxQ=
github.com/cloudogu/k8s-dogu-lib/v2 v2.0.0-20250924122244-01339f784cd0 h1:ohitXnpL655pmZOO0LVjW/weVi1ftFKSXG7jllbRQag=
github.com/cloudogu/k8s-dogu-lib/v2 v2.0.0-20250924122244-01339f784cd0/go.mod h1:2xkJ1TI7fuBRcqIpHUlmQ6CJAtzlOvyUbwPktIVq6K8=
github.com/cloudogu/k8s-registry-lib v0.2.2-0.20250818112109-cfd93e57e9f6 h1:OYMO6DYX6rtzo2a88hXXTzwf8+aXt7uV+BTaEmvlls4=
Expand Down
13 changes: 13 additions & 0 deletions k8s/helm/templates/debug-mode-reader-role.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
labels:
{{- include "k8s-blueprint-operator.labels" . | nindent 4 }}
name: {{ include "k8s-blueprint-operator.name" . }}-debug-mode-reader-role
rules:
- apiGroups:
- k8s.cloudogu.com
resources:
- debugmodes
verbs:
- get
13 changes: 13 additions & 0 deletions k8s/helm/templates/debug-mode-reader-rolebinding.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
{{- include "k8s-blueprint-operator.labels" . | nindent 4 }}
name: {{ include "k8s-blueprint-operator.name" . }}-debug-mode-reader-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ include "k8s-blueprint-operator.name" . }}-debug-mode-reader-role
subjects:
- kind: ServiceAccount
name: {{ include "k8s-blueprint-operator.name" . }}-controller-manager
2 changes: 2 additions & 0 deletions k8s/helm/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ spec:
name: ces-proxy
key: url
optional: true
- name: DEBOUNCE_WINDOW
value: {{ quote .Values.manager.reconciler.debounceWindow | default "10s" }}
image: "{{ .Values.manager.image.registry }}/{{ .Values.manager.image.repository }}:{{ .Values.manager.image.tag | default .Chart.AppVersion }}"
livenessProbe:
httpGet:
Expand Down
20 changes: 18 additions & 2 deletions k8s/helm/templates/manager-role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,26 @@ rules:
resources:
- secrets
verbs:
- create
- update
- get
- list # needed, as the registry-lib seems to need that for a normal get command
- watch
# issue PVC read-only permissions to amend the Blueprint CR with a currently configured Dogu volume size
- apiGroups:
- ""
resources:
- configmaps # for normal dogu config
verbs:
- get
- list # needed, as the registry-lib seems to need that for a normal get command
- watch
- apiGroups:
- k8s.cloudogu.com
resources:
- dogus
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
Expand Down
2 changes: 2 additions & 0 deletions k8s/helm/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ manager:
memory: 105M
networkPolicies:
enabled: true
reconciler:
debounceWindow: 10s
doguRegistry:
certificate:
secret: dogu-registry-cert
2 changes: 2 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/cloudogu/k8s-blueprint-operator/v2/pkg"
"github.com/cloudogu/k8s-blueprint-operator/v2/pkg/adapter/reconciler"
"github.com/cloudogu/k8s-blueprint-operator/v2/pkg/config"
v2 "github.com/cloudogu/k8s-dogu-lib/v2/api/v2"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
Expand Down Expand Up @@ -46,6 +47,7 @@ func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))

utilruntime.Must(bpv2.AddToScheme(scheme))
utilruntime.Must(v2.AddToScheme(scheme))
// +kubebuilder:scaffold:scheme
}

Expand Down
7 changes: 6 additions & 1 deletion main_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"testing"

"github.com/go-logr/logr"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -181,6 +180,7 @@ func Test_startOperator(t *testing.T) {
t.Setenv("DOGU_REGISTRY_ENDPOINT", "dogu.example.com")
t.Setenv("DOGU_REGISTRY_USERNAME", "user")
t.Setenv("DOGU_REGISTRY_PASSWORD", "password")
t.Setenv("DEBOUNCE_WINDOW", "10s")

oldNewManagerFunc := ctrl.NewManager
oldGetConfigFunc := ctrl.GetConfigOrDie
Expand All @@ -196,6 +196,7 @@ func Test_startOperator(t *testing.T) {
//ctrlManMock.EXPECT().GetConfig().Return(restConfig)
ctrlManMock.EXPECT().GetControllerOptions().Return(config.Controller{})
ctrlManMock.EXPECT().GetScheme().Return(runtime.NewScheme())
ctrlManMock.EXPECT().GetCache().Return(nil)

ctrl.NewManager = func(config *rest.Config, options manager.Options) (manager.Manager, error) {
return ctrlManMock, nil
Expand All @@ -220,6 +221,7 @@ func Test_startOperator(t *testing.T) {
t.Setenv("DOGU_REGISTRY_ENDPOINT", "dogu.example.com")
t.Setenv("DOGU_REGISTRY_USERNAME", "user")
t.Setenv("DOGU_REGISTRY_PASSWORD", "password")
t.Setenv("DEBOUNCE_WINDOW", "10s")

oldNewManagerFunc := ctrl.NewManager
oldGetConfigFunc := ctrl.GetConfigOrDie
Expand Down Expand Up @@ -268,6 +270,7 @@ func Test_startOperator(t *testing.T) {
t.Setenv("DOGU_REGISTRY_ENDPOINT", "dogu.example.com")
t.Setenv("DOGU_REGISTRY_USERNAME", "user")
t.Setenv("DOGU_REGISTRY_PASSWORD", "password")
t.Setenv("DEBOUNCE_WINDOW", "10s")

oldNewManagerFunc := ctrl.NewManager
oldGetConfigFunc := ctrl.GetConfigOrDie
Expand Down Expand Up @@ -317,6 +320,7 @@ func Test_startOperator(t *testing.T) {
t.Setenv("DOGU_REGISTRY_ENDPOINT", "dogu.example.com")
t.Setenv("DOGU_REGISTRY_USERNAME", "user")
t.Setenv("DOGU_REGISTRY_PASSWORD", "password")
t.Setenv("DEBOUNCE_WINDOW", "10s")

oldNewManagerFunc := ctrl.NewManager
oldGetConfigFunc := ctrl.GetConfigOrDie
Expand Down Expand Up @@ -372,6 +376,7 @@ func Test_startOperator(t *testing.T) {
t.Setenv("DOGU_REGISTRY_ENDPOINT", "dogu.example.com")
t.Setenv("DOGU_REGISTRY_USERNAME", "user")
t.Setenv("DOGU_REGISTRY_PASSWORD", "password")
t.Setenv("DEBOUNCE_WINDOW", "10s")

oldNewManagerFunc := ctrl.NewManager
oldGetConfigFunc := ctrl.GetConfigOrDie
Expand Down
16 changes: 16 additions & 0 deletions pkg/adapter/kubernetes/blueprintcr/v2/blueprintSpecCRRepository.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,22 @@ func (repo *blueprintSpecRepo) CheckSingleton(ctx context.Context) error {
}
}

func (repo *blueprintSpecRepo) ListIds(ctx context.Context) ([]string, error) {
list, err := repo.blueprintClient.List(ctx, metav1.ListOptions{})
if err != nil {
return nil, &domainservice.InternalError{
WrappedError: err,
Message: "error while listing blueprint resources",
}
}

result := make([]string, len(list.Items))
for index, blueprint := range list.Items {
result[index] = blueprint.Name
}
return result, nil
}

func (repo *blueprintSpecRepo) serializeBlueprintAndMask(blueprintSpec *domain.BlueprintSpec, blueprintCR *v2.Blueprint, blueprintId string) error {
blueprint, blueprintErr := serializerv2.ConvertToBlueprintDomain(blueprintCR.Spec.Blueprint)
if blueprintErr != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ func Test_blueprintSpecRepo_CheckSingleton(t *testing.T) {
eventRecorderMock := newMockEventRecorder(t)
repo := NewBlueprintSpecRepository(restClientMock, eventRecorderMock)

restClientMock.EXPECT().List(ctx, metav1.ListOptions{Limit: 2}).Return(nil, nil)
restClientMock.EXPECT().List(ctx, mock.Anything).Return(nil, nil)

// when
err := repo.CheckSingleton(ctx)
Expand All @@ -413,7 +413,7 @@ func Test_blueprintSpecRepo_CheckSingleton(t *testing.T) {
restClientMock := newMockBlueprintInterface(t)
eventRecorderMock := newMockEventRecorder(t)
repo := NewBlueprintSpecRepository(restClientMock, eventRecorderMock)
restClientMock.EXPECT().List(ctx, metav1.ListOptions{Limit: 2}).Return(&bpv2.BlueprintList{}, nil)
restClientMock.EXPECT().List(ctx, mock.Anything).Return(&bpv2.BlueprintList{}, nil)

// when
err := repo.CheckSingleton(ctx)
Expand All @@ -435,7 +435,7 @@ func Test_blueprintSpecRepo_CheckSingleton(t *testing.T) {
},
},
}
restClientMock.EXPECT().List(ctx, metav1.ListOptions{Limit: 2}).Return(list, nil)
restClientMock.EXPECT().List(ctx, mock.Anything).Return(list, nil)

// when
err := repo.CheckSingleton(ctx)
Expand All @@ -462,7 +462,7 @@ func Test_blueprintSpecRepo_CheckSingleton(t *testing.T) {
},
},
}
restClientMock.EXPECT().List(ctx, metav1.ListOptions{Limit: 2}).Return(list, nil)
restClientMock.EXPECT().List(ctx, mock.Anything).Return(list, nil)

// when
err := repo.CheckSingleton(ctx)
Expand All @@ -479,7 +479,7 @@ func Test_blueprintSpecRepo_CheckSingleton(t *testing.T) {
eventRecorderMock := newMockEventRecorder(t)
repo := NewBlueprintSpecRepository(restClientMock, eventRecorderMock)

restClientMock.EXPECT().List(ctx, metav1.ListOptions{Limit: 2}).Return(nil, assert.AnError)
restClientMock.EXPECT().List(ctx, mock.Anything).Return(nil, assert.AnError)

// when
err := repo.CheckSingleton(ctx)
Expand All @@ -491,3 +491,52 @@ func Test_blueprintSpecRepo_CheckSingleton(t *testing.T) {
assert.ErrorContains(t, err, "error while listing blueprint resources")
})
}

func Test_blueprintSpecRepo_ListIds(t *testing.T) {
t.Run("success", func(t *testing.T) {
restClientMock := newMockBlueprintInterface(t)
repo := NewBlueprintSpecRepository(restClientMock, nil)

returnList := bpv2.BlueprintList{Items: []bpv2.Blueprint{
{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "test2",
},
},
}}

restClientMock.EXPECT().List(ctx, metav1.ListOptions{}).Return(&returnList, nil)

// when
idList, err := repo.ListIds(ctx)

// then
require.NoError(t, err)
assert.Len(t, idList, 2)
assert.Contains(t, idList, "test1")
assert.Contains(t, idList, "test2")
})

t.Run("throw internal error on list error", func(t *testing.T) {
restClientMock := newMockBlueprintInterface(t)
repo := NewBlueprintSpecRepository(restClientMock, nil)

restClientMock.EXPECT().List(ctx, metav1.ListOptions{}).Return(nil, assert.AnError)

// when
list, err := repo.ListIds(ctx)

// then
require.Error(t, err)
assert.Nil(t, list)
var targetErr *domainservice.InternalError
assert.ErrorAs(t, err, &targetErr)
assert.ErrorContains(t, err, "error while listing blueprint resources")
})

}
39 changes: 39 additions & 0 deletions pkg/adapter/kubernetes/debugmodecr/debugModeRepo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package debugmodecr

import (
"context"
"fmt"

"github.com/cloudogu/k8s-blueprint-operator/v2/pkg/domain/ecosystem"
"github.com/cloudogu/k8s-blueprint-operator/v2/pkg/domainservice"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const debugModeSingletonCRName = "debug-mode"

type debugModeRepo struct {
debugModeClient DebugModeInterface
}

// NewDebugModeRepo returns a new debugModeRepo to interact with the debug mode CR.
func NewDebugModeRepo(debugModeClient DebugModeInterface) domainservice.DebugModeRepository {
return &debugModeRepo{debugModeClient: debugModeClient}
}

func (repo *debugModeRepo) GetSingleton(ctx context.Context) (*ecosystem.DebugMode, error) {
cr, err := repo.debugModeClient.Get(ctx, debugModeSingletonCRName, metav1.GetOptions{})
if err != nil {
if k8sErrors.IsNotFound(err) {
return nil, &domainservice.NotFoundError{
WrappedError: err,
Message: fmt.Sprintf("cannot load debug mode CR %q as it does not exist", debugModeSingletonCRName),
}
}
return nil, &domainservice.InternalError{
WrappedError: err,
Message: fmt.Sprintf("error while loading debug mode CR %q", debugModeSingletonCRName),
}
}
return parseDebugModeCR(cr)
}
Loading