Skip to content

Commit 4064f2d

Browse files
committed
test: adds testing
1 parent 4fb0b5d commit 4064f2d

File tree

16 files changed

+770
-38
lines changed

16 files changed

+770
-38
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
tmp
2+
external-crds
23

34
# Binaries for programs and plugins
45
*.exe

Taskfile.yaml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,23 @@ includes:
44
shared:
55
taskfile: hack/common/Taskfile_controller.yaml
66
flatten: true
7+
excludes: # put task names in here which are overwritten in this file
8+
- generate:code
9+
vars:
10+
NESTED_MODULES: ""
11+
API_DIRS: "{{.ROOT_DIR}}/api/..."
12+
MANIFEST_OUT: "{{.ROOT_DIR}}/api/crds/manifests"
13+
CODE_DIRS: "{{.ROOT_DIR}}/cmd/... {{.ROOT_DIR}}/internal/... {{.ROOT_DIR}}/api/..."
14+
COMPONENTS: "usage-operator"
15+
REPO_URL: "https://github.com/openmcp-project/usage-operator"
16+
GENERATE_DOCS_INDEX: "true"
17+
CHART_COMPONENTS: "[]"
18+
ENVTEST_REQUIRED: "true"
19+
common: # imported a second time so that overwriting task definitions can call the overwritten task with a 'c:' prefix
20+
taskfile: hack/common/Taskfile_controller.yaml
21+
internal: true
22+
aliases:
23+
- c
724
excludes: [] # put task names in here which are overwritten in this file
825
vars:
926
NESTED_MODULES: ""
@@ -15,3 +32,22 @@ includes:
1532
GENERATE_DOCS_INDEX: "true"
1633
CHART_COMPONENTS: "[]"
1734
ENVTEST_REQUIRED: "true"
35+
36+
tasks:
37+
generate:code: # overwrites shared code task to add external API fetching
38+
desc: " Generate code (mainly DeepCopy functions) and fetches external APIs."
39+
aliases:
40+
- gen:code
41+
- g:code
42+
run: once
43+
cmds:
44+
- task: download-crds
45+
- task: c:generate:code
46+
47+
download-crds:
48+
internal: true
49+
cmds:
50+
- gh api https://api.github.com/repos/openmcp-project/mcp-operator/contents/api/crds/manifests | jq -r '.[] | select(.type=="file") | .download_url' | xargs -n 1 curl -s -O -J
51+
- gh api https://api.github.com/repos/openmcp-project/project-workspace-operator/contents/api/crds/manifests | jq -r '.[] | select(.type=="file") | .download_url' | xargs -n 1 curl -s -O -J
52+
dir: external-crds
53+
desc: "Download CRD files from mcp-operator GitHub repository"

api/crds/manifests/usage.openmcp.cloud_mcpusages.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ spec:
8787
type: string
8888
required:
8989
- charging_target
90-
- daily_usage
9190
- mcp
9291
- project
9392
- workspace

api/usage/v1/mcpusage_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ type MCPUsageSpec struct {
3232
Project string `json:"project"`
3333
Workspace string `json:"workspace"`
3434
MCP string `json:"mcp"`
35-
Usage []DailyUsage `json:"daily_usage"`
35+
Usage []DailyUsage `json:"daily_usage,omitempty"`
3636
LastUsageCaptured metav1.Time `json:"last_usage_captured,omitempty"`
3737
MCPCreatedAt metav1.Time `json:"mcp_created_at,omitempty"`
3838
MCPDeletedAt metav1.Time `json:"mcp_deleted_at,omitempty"`

cmd/usage-operator/app/run.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ func (o *RunOptions) Run(ctx context.Context) error {
275275
return fmt.Errorf("unable to create manager: %w", err)
276276
}
277277

278-
usageTracker, err := usage.NewUsageTracker(&o.Log, mgr.GetClient())
278+
usageTracker, err := usage.NewUsageTracker(mgr.GetClient())
279279
if err != nil {
280280
return fmt.Errorf("unable to create usage tracker: %w", err)
281281
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/openmcp-project/usage-operator
33
go 1.24.4
44

55
require (
6+
github.com/go-logr/logr v1.4.3
67
github.com/google/uuid v1.6.0
78
github.com/onsi/ginkgo/v2 v2.23.4
89
github.com/onsi/gomega v1.37.0
@@ -33,7 +34,6 @@ require (
3334
github.com/felixge/httpsnoop v1.0.4 // indirect
3435
github.com/fsnotify/fsnotify v1.8.0 // indirect
3536
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
36-
github.com/go-logr/logr v1.4.3 // indirect
3737
github.com/go-logr/stdr v1.2.2 // indirect
3838
github.com/go-logr/zapr v1.3.0 // indirect
3939
github.com/go-openapi/jsonpointer v0.21.0 // indirect

internal/controller/managedcontrolplane_controller.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121
"errors"
2222
"regexp"
2323

24-
"github.com/openmcp-project/controller-utils/pkg/logging"
24+
"github.com/go-logr/logr"
2525
corev1alpha1 "github.com/openmcp-project/mcp-operator/api/core/v1alpha1"
2626
"k8s.io/apimachinery/pkg/runtime"
2727
ctrl "sigs.k8s.io/controller-runtime"
@@ -52,7 +52,7 @@ type ManagedControlPlaneReconciler struct {
5252
// For more details, check Reconcile and its Result here:
5353
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile
5454
func (r *ManagedControlPlaneReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
55-
log, err := logging.FromContext(ctx)
55+
log, err := logr.FromContext(ctx)
5656
if err != nil {
5757
return ctrl.Result{}, client.IgnoreNotFound(err)
5858
}
@@ -75,10 +75,10 @@ func (r *ManagedControlPlaneReconciler) Reconcile(ctx context.Context, req ctrl.
7575
project := matches[1]
7676
workspace := matches[2]
7777

78-
log.Info("mcp '" + mcp.Name + "' status '" + string(mcp.Status.Status) + "'")
78+
log.Info("reconcile", "mcp", mcp.Name, "status", string(mcp.Status.Status))
7979

8080
if mcp.GetDeletionTimestamp() != nil || mcp.Status.Status == corev1alpha1.MCPStatusDeleting {
81-
log.Info("mcp '" + mcp.Name + "' was deleted. Tracking it...")
81+
log.Info("mcp was deleted", "mcp", mcp.Name)
8282
err := r.UsageTracker.DeletionEvent(ctx, project, workspace, mcp.Name)
8383
if err != nil {
8484
log.Error(err, "error when tracking deletion")

internal/controller/managedcontrolplane_controller_test.go

Lines changed: 129 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,141 @@ limitations under the License.
1717
package controller
1818

1919
import (
20+
"context"
21+
"strings"
22+
"time"
23+
2024
. "github.com/onsi/ginkgo/v2"
25+
. "github.com/onsi/gomega"
26+
corev1 "k8s.io/api/core/v1"
27+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+
"sigs.k8s.io/controller-runtime/pkg/client"
29+
30+
corev1alpha1 "github.com/openmcp-project/mcp-operator/api/core/v1alpha1"
31+
pwcorev1alpha1 "github.com/openmcp-project/project-workspace-operator/api/core/v1alpha1"
32+
33+
v1 "github.com/openmcp-project/usage-operator/api/usage/v1"
34+
)
35+
36+
const (
37+
ProjectName = "project"
38+
WorkspaceName = "workspace"
39+
MCPName = "test-mcp"
40+
41+
ChargingTarget = "12345678"
42+
43+
timeout = time.Second * 10
44+
duration = time.Second * 10
45+
interval = time.Millisecond * 250
2146
)
2247

23-
var _ = Describe("ManagedControlPlane Controller", func() {
48+
var (
49+
projectNamespaceName string
50+
workspaceNamespaceName string
51+
52+
mcpUsageName string
53+
)
54+
55+
var _ = Describe("ManagedControlPlane Controller", Ordered, func() {
2456
Context("When reconciling a resource", func() {
57+
BeforeAll(func() {
58+
ctx := context.Background()
59+
projectNamespaceName = "project-" + ProjectName
60+
workspaceNamespaceName = projectNamespaceName + "--ws-" + WorkspaceName
61+
namespaces := []corev1.Namespace{
62+
{
63+
ObjectMeta: metav1.ObjectMeta{
64+
Name: projectNamespaceName,
65+
},
66+
},
67+
{
68+
ObjectMeta: metav1.ObjectMeta{
69+
Name: workspaceNamespaceName,
70+
},
71+
},
72+
}
73+
Expect(k8sClient.Create(ctx, &namespaces[0])).To(Succeed())
74+
Expect(k8sClient.Create(ctx, &namespaces[1])).To(Succeed())
75+
76+
project := pwcorev1alpha1.Project{
77+
ObjectMeta: metav1.ObjectMeta{
78+
Name: ProjectName,
79+
Labels: map[string]string{
80+
"openmcp.cloud.sap/charging-target": ChargingTarget,
81+
},
82+
},
83+
}
84+
Expect(k8sClient.Create(ctx, &project)).To(Succeed())
85+
workspace := pwcorev1alpha1.Workspace{
86+
ObjectMeta: metav1.ObjectMeta{
87+
Name: WorkspaceName,
88+
Namespace: projectNamespaceName,
89+
},
90+
}
91+
Expect(k8sClient.Create(ctx, &workspace)).To(Succeed())
92+
mcp := corev1alpha1.ManagedControlPlane{
93+
ObjectMeta: metav1.ObjectMeta{
94+
Name: MCPName,
95+
Namespace: workspaceNamespaceName,
96+
},
97+
}
98+
Expect(k8sClient.Create(ctx, &mcp)).To(Succeed())
99+
})
100+
101+
It("should create a mcp usage resource based on a ManagedControlPlane resource", func() {
102+
ctx := context.Background()
103+
104+
var mcpUsages v1.MCPUsageList
105+
Eventually(func(g Gomega) {
106+
g.Expect(k8sClient.List(ctx, &mcpUsages)).To(Succeed())
107+
108+
g.Expect(mcpUsages.Items).Should(HaveLen(1))
109+
110+
mcpUsageName = mcpUsages.Items[0].Name
111+
112+
g.Expect(mcpUsages.Items[0].Spec.ChargingTarget).Should(Equal(ChargingTarget))
113+
}, timeout, interval).Should(Succeed())
114+
})
115+
116+
It("should have set the right charging target", func() {
117+
ctx := context.Background()
118+
119+
mcpUsage := v1.MCPUsage{
120+
ObjectMeta: metav1.ObjectMeta{
121+
Name: mcpUsageName,
122+
},
123+
}
124+
Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(&mcpUsage), &mcpUsage)).Should(Succeed())
125+
126+
Expect(mcpUsage.Spec.ChargingTarget).Should(Equal(ChargingTarget))
127+
})
128+
129+
It("should mark a mcp usage resource as deleted when ManagedControlPlane is deleted", func() {
130+
ctx := context.Background()
131+
132+
mcpUsage := v1.MCPUsage{
133+
ObjectMeta: metav1.ObjectMeta{
134+
Name: mcpUsageName,
135+
},
136+
}
137+
Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(&mcpUsage), &mcpUsage)).Should(Succeed())
138+
139+
var mcp corev1alpha1.ManagedControlPlane
140+
Expect(k8sClient.Get(ctx, client.ObjectKey{
141+
Name: strings.ToLower(MCPName),
142+
Namespace: workspaceNamespaceName,
143+
}, &mcp)).Should(Succeed())
144+
mcp.Status.Status = corev1alpha1.MCPStatusDeleting
145+
Expect(k8sClient.Status().Update(ctx, &mcp)).Should(Succeed())
146+
147+
var mcpUsages v1.MCPUsageList
148+
Eventually(func(g Gomega) {
149+
g.Expect(k8sClient.List(ctx, &mcpUsages)).To(Succeed())
25150

26-
It("should successfully reconcile the resource", func() {
151+
g.Expect(mcpUsages.Items).Should(HaveLen(1))
27152

28-
// TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
29-
// Example: If you expect a certain status condition after reconciliation, verify it here.
153+
g.Expect(mcpUsages.Items[0].Spec.MCPDeletedAt.IsZero()).Should(BeFalse())
154+
}, timeout, interval).Should(Succeed())
30155
})
31156
})
32157
})

internal/controller/suite_test.go

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,23 @@ import (
2222
"path/filepath"
2323
"testing"
2424

25+
ctrl "sigs.k8s.io/controller-runtime"
26+
2527
. "github.com/onsi/ginkgo/v2"
2628
. "github.com/onsi/gomega"
2729

2830
corev1alpha1 "github.com/openmcp-project/mcp-operator/api/core/v1alpha1"
31+
pwcorev1alpha1 "github.com/openmcp-project/project-workspace-operator/api/core/v1alpha1"
32+
2933
"k8s.io/client-go/kubernetes/scheme"
3034
"k8s.io/client-go/rest"
3135
"sigs.k8s.io/controller-runtime/pkg/client"
3236
"sigs.k8s.io/controller-runtime/pkg/envtest"
3337
logf "sigs.k8s.io/controller-runtime/pkg/log"
3438
"sigs.k8s.io/controller-runtime/pkg/log/zap"
39+
40+
v1 "github.com/openmcp-project/usage-operator/api/usage/v1"
41+
"github.com/openmcp-project/usage-operator/internal/usage"
3542
// +kubebuilder:scaffold:imports
3643
)
3744

@@ -60,12 +67,21 @@ var _ = BeforeSuite(func() {
6067
var err error
6168
err = corev1alpha1.AddToScheme(scheme.Scheme)
6269
Expect(err).NotTo(HaveOccurred())
70+
err = pwcorev1alpha1.AddToScheme(scheme.Scheme)
71+
Expect(err).NotTo(HaveOccurred())
72+
err = v1.AddToScheme(scheme.Scheme)
73+
Expect(err).NotTo(HaveOccurred())
6374

6475
// +kubebuilder:scaffold:scheme
6576

6677
By("bootstrapping test environment")
6778
testEnv = &envtest.Environment{
68-
CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
79+
CRDDirectoryPaths: []string{
80+
filepath.Join("..", "..", "api", "crds", "manifests"),
81+
// Add external CRD directories here:
82+
filepath.Join("..", "..", "external-crds"),
83+
// Add more paths as needed
84+
},
6985
ErrorIfCRDPathMissing: false,
7086
}
7187

@@ -82,6 +98,27 @@ var _ = BeforeSuite(func() {
8298
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
8399
Expect(err).NotTo(HaveOccurred())
84100
Expect(k8sClient).NotTo(BeNil())
101+
102+
k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{
103+
Scheme: scheme.Scheme,
104+
})
105+
Expect(err).ToNot(HaveOccurred())
106+
107+
usageTracker, err := usage.NewUsageTracker(k8sClient)
108+
Expect(err).NotTo(HaveOccurred())
109+
110+
err = (&ManagedControlPlaneReconciler{
111+
Client: k8sManager.GetClient(),
112+
Scheme: k8sManager.GetScheme(),
113+
UsageTracker: usageTracker,
114+
}).SetupWithManager(k8sManager)
115+
Expect(err).ToNot(HaveOccurred())
116+
117+
go func() {
118+
defer GinkgoRecover()
119+
err = k8sManager.Start(ctx)
120+
Expect(err).ToNot(HaveOccurred(), "failed to run manager")
121+
}()
85122
})
86123

87124
var _ = AfterSuite(func() {

0 commit comments

Comments
 (0)