Skip to content

Commit ad4beed

Browse files
fix: Deletion of cluster labels don't work (#48)
Signed-off-by: Kasprzak, Mikolaj <[email protected]> Co-authored-by: Daniecki, Jozef <[email protected]>
1 parent c6e8236 commit ad4beed

File tree

9 files changed

+111
-31
lines changed

9 files changed

+111
-31
lines changed

internal/k8s/client.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -240,28 +240,32 @@ func (cli *Client) DeleteNamespace(ctx context.Context, namespace string) error
240240
return cli.Dyn.Resource(namespaceRes).Delete(ctx, namespace, deleteOptions)
241241
}
242242

243-
// CreateClusterLabels creates new labels on the cluster object in the given namespace
244-
func (cli *Client) CreateClusterLabels(ctx context.Context, namespace string, clusterName string, newLabels map[string]string) error {
245-
if newLabels == nil {
243+
// SetClusterLabels overrides the labels of the cluster object in the given namespace
244+
func (cli *Client) SetClusterLabels(ctx context.Context, namespace string, clusterName string, newUserLabels map[string]string) error {
245+
if newUserLabels == nil {
246246
return nil
247247
}
248248

249-
return createLabels(ctx, cli, namespace, clusterResourceSchema, clusterName, newLabels)
249+
return modifyLabels(ctx, cli, namespace, clusterResourceSchema, clusterName, func(cluster *unstructured.Unstructured) {
250+
cluster.SetLabels(labels.Merge(labels.SystemLabels(cluster.GetLabels()), newUserLabels))
251+
})
250252
}
251253

252-
// CreateTemplateLabels creates new labels on the template object in the given namespace
253-
func (cli *Client) CreateTemplateLabels(ctx context.Context, namespace string, templateName string, newLabels map[string]string) error {
254+
// AddTemplateLabels appends new labels on the template object in the given namespace
255+
func (cli *Client) AddTemplateLabels(ctx context.Context, namespace string, templateName string, newLabels map[string]string) error {
254256
if newLabels == nil {
255257
return nil
256258
}
257259

258-
return createLabels(ctx, cli, namespace, templateResourceSchema, templateName, newLabels)
260+
return modifyLabels(ctx, cli, namespace, templateResourceSchema, templateName, func(template *unstructured.Unstructured) {
261+
template.SetLabels(labels.Merge(template.GetLabels(), newLabels))
262+
})
259263
}
260264

261-
// createLabels creates new labels on the resource object in the given namespace
265+
// modifyLabels modifies the labels of the given resource in the given namespace
262266
// It retries on transient "the object has been modified" error, which is expected when the cluster object was updated by another process after we fetched it
263267
// It returns an error if the operation fails after all retries
264-
func createLabels(ctx context.Context, cli *Client, namespace string, resourceSchema schema.GroupVersionResource, resourceName string, newLabels map[string]string) error {
268+
func modifyLabels(ctx context.Context, cli *Client, namespace string, resourceSchema schema.GroupVersionResource, resourceName string, op func(*unstructured.Unstructured)) error {
265269
transientError := func(err error) bool {
266270
tryAgainErrPattern := "the object has been modified; please apply your changes to the latest version and try again"
267271
return strings.Contains(err.Error(), tryAgainErrPattern)
@@ -272,7 +276,7 @@ func createLabels(ctx context.Context, cli *Client, namespace string, resourceSc
272276
if err != nil {
273277
return backoff.Permanent(err)
274278
}
275-
resource.SetLabels(labels.Merge(resource.GetLabels(), newLabels))
279+
op(resource)
276280
if _, err = cli.Dyn.Resource(resourceSchema).Namespace(namespace).Update(ctx, resource, metav1.UpdateOptions{}); err != nil {
277281
if transientError(err) {
278282
return err // retry on transient error

internal/labels/labels.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const (
2020
)
2121

2222
var (
23-
systemPrefixes = []string{PlatformPrefix, capiDomainLabelKey, capiTopologyLabelKey, PrometheusMetricsUrlLabelKey}
23+
systemPrefixes = []string{PlatformPrefix, capiDomainLabelKey, capiTopologyLabelKey, PrometheusMetricsUrlLabelKey, TrustedComputeLabelKey}
2424
labelKeyRegex = regexp.MustCompile(`^(([A-Za-z0-9][-A-Za-z0-9_.]{0,250})?[A-Za-z0-9]\/)?([A-Za-z0-9][-A-Za-z0-9_.]{0,61})?[A-Za-z0-9]$`)
2525
labelValueRegex = regexp.MustCompile(`^([A-Za-z0-9][-A-Za-z0-9_.]{0,61})?[A-Za-z0-9]?$`)
2626
)
@@ -29,10 +29,8 @@ func OverrideSystemPrefixes(prefixes []string) {
2929
systemPrefixes = prefixes
3030
}
3131

32-
// Filter returns new map with only user defined labels
33-
func Filter(clusterLabels map[string]string) map[string]string {
34-
f := map[string]string{}
35-
32+
// UserLabels returns new map with only user defined labels
33+
func UserLabels(clusterLabels map[string]string) map[string]string {
3634
keep := func(s string) bool {
3735
for _, p := range systemPrefixes {
3836
if strings.HasPrefix(s, p) {
@@ -42,7 +40,27 @@ func Filter(clusterLabels map[string]string) map[string]string {
4240
return true
4341
}
4442

45-
for key, value := range clusterLabels {
43+
return filter(clusterLabels, keep)
44+
}
45+
46+
// SystemLabels returns new map with only system defined labels
47+
func SystemLabels(clusterLabels map[string]string) map[string]string {
48+
keep := func(s string) bool {
49+
for _, p := range systemPrefixes {
50+
if strings.HasPrefix(s, p) {
51+
return true
52+
}
53+
}
54+
return false
55+
}
56+
57+
return filter(clusterLabels, keep)
58+
}
59+
60+
func filter(labels map[string]string, keep func(string) bool) map[string]string {
61+
f := map[string]string{}
62+
63+
for key, value := range labels {
4664
if !keep(key) {
4765
continue
4866
}

internal/labels/labels_test.go

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"github.com/open-edge-platform/cluster-manager/v2/internal/labels"
1111
)
1212

13-
func TestFilter(t *testing.T) {
13+
func TestUserLabels(t *testing.T) {
1414
clusterLabels := map[string]string{
1515
"edge-orchestrator.intel.com/cluster-id": "cluster-479656a6",
1616
"edge-orchestrator.intel.com/clustername": "scale-0a9c889f-97a4-5a27-aa57-e82300a2b19c",
@@ -25,6 +25,7 @@ func TestFilter(t *testing.T) {
2525
"default": "true",
2626
"edge-orchestrator.intel.com/users-label": "user-value",
2727
"topology.cluster.x-k8s.io/owned": "",
28+
"trusted-compute-compatible": "true",
2829
}
2930

3031
want := map[string]string{
@@ -35,7 +36,43 @@ func TestFilter(t *testing.T) {
3536
"default": "true",
3637
}
3738

38-
got := labels.Filter(clusterLabels)
39+
got := labels.UserLabels(clusterLabels)
40+
if diff := cmp.Diff(want, got); diff != "" {
41+
t.Errorf("filter mismatch (-want +got):\n%s", diff)
42+
}
43+
}
44+
45+
func TestSystemLabels(t *testing.T) {
46+
clusterLabels := map[string]string{
47+
"edge-orchestrator.intel.com/cluster-id": "cluster-479656a6",
48+
"edge-orchestrator.intel.com/clustername": "scale-0a9c889f-97a4-5a27-aa57-e82300a2b19c",
49+
"edge-orchestrator.intel.com/project-id": "0ba9d4dc-c4c2-4904-b526-57ace6e76922",
50+
"edge-orchestrator.intel.com/template": "scaletest-v0.0.2",
51+
"clustername": "scale-0a9c889f-97a4-5a27-aa57-e82300a2b19c",
52+
"cpumanager": "true",
53+
"prometheusMetricsURL": "metrics-node.scale.espd.infra-host.com",
54+
"test-app": "enabled",
55+
"tests": "scale",
56+
"cluster.x-k8s.io/cluster-name": "demo-cluster",
57+
"default": "true",
58+
"edge-orchestrator.intel.com/users-label": "user-value",
59+
"topology.cluster.x-k8s.io/owned": "",
60+
"trusted-compute-compatible": "false",
61+
}
62+
63+
want := map[string]string{
64+
"edge-orchestrator.intel.com/cluster-id": "cluster-479656a6",
65+
"edge-orchestrator.intel.com/clustername": "scale-0a9c889f-97a4-5a27-aa57-e82300a2b19c",
66+
"edge-orchestrator.intel.com/project-id": "0ba9d4dc-c4c2-4904-b526-57ace6e76922",
67+
"edge-orchestrator.intel.com/template": "scaletest-v0.0.2",
68+
"prometheusMetricsURL": "metrics-node.scale.espd.infra-host.com",
69+
"cluster.x-k8s.io/cluster-name": "demo-cluster",
70+
"edge-orchestrator.intel.com/users-label": "user-value",
71+
"topology.cluster.x-k8s.io/owned": "",
72+
"trusted-compute-compatible": "false",
73+
}
74+
75+
got := labels.SystemLabels(clusterLabels)
3976
if diff := cmp.Diff(want, got); diff != "" {
4077
t.Errorf("filter mismatch (-want +got):\n%s", diff)
4178
}

internal/multitenancy/multitenancy.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ func (tdm *TenancyDatamodel) setupProject(ctx context.Context, project *nexus.Ru
170170
}
171171

172172
labels := map[string]string{labels.DefaultLabelKey: labels.DefaultLabelVal}
173-
if err = tdm.k8s.CreateTemplateLabels(ctx, projectId, defaultTemplateName, labels); err != nil {
173+
if err = tdm.k8s.AddTemplateLabels(ctx, projectId, defaultTemplateName, labels); err != nil {
174174
return fmt.Errorf("failed to label default template: %w", err)
175175
}
176176

internal/rest/getv2clusters.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ func (s *Server) convertClusters(ctx context.Context, namespace string, unstruct
125125
// get machines associated with the cluster
126126
machines := getClusterMachines(allMachines, capiCluster.Name)
127127

128-
labels := labels.Filter(capiCluster.Labels)
128+
labels := labels.UserLabels(capiCluster.Labels)
129129
unstructuredLabels := convert.MapStringToAny(labels)
130130

131131
lp, errs := getClusterLifecyclePhase(&capiCluster)

internal/rest/getv2clustersclusterdetail.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func (s *Server) GetV2ClustersNodeIdClusterdetail(ctx context.Context, request a
3030
},
3131
}, nil
3232
}
33-
labels := labels.Filter(convert.MapAnyToString(*clusterDetails.Labels))
33+
labels := labels.UserLabels(convert.MapAnyToString(*clusterDetails.Labels))
3434
unstructuredLabels := convert.MapStringToAny(labels)
3535
clusterDetails.Labels = &unstructuredLabels
3636
return api.GetV2ClustersNodeIdClusterdetail200JSONResponse(clusterDetails), nil

internal/rest/getv2clustersname.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ func (s *Server) getCluster(ctx context.Context, activeProjectID, name string) (
9797
slog.Error("failed to fetch machines for cluster", "cluster", capiCluster.Name, "error", err)
9898
}
9999

100-
labels := labels.Filter(capiCluster.Labels)
100+
labels := labels.UserLabels(capiCluster.Labels)
101101
unstrucutreLabels := convert.MapStringToAny(labels)
102102

103103
nodes, err := cluster.Nodes(ctx, cli, capiCluster)

internal/rest/putv2clusterlabels.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ func (s *Server) PutV2ClustersNameLabels(ctx context.Context, request api.PutV2C
3838
}, nil
3939
}
4040

41-
newLabels := *request.Body.Labels
42-
if !labels.Valid(newLabels) {
41+
newUserLabels := *request.Body.Labels
42+
if !labels.Valid(newUserLabels) {
4343
errMsg := "invalid cluster label keys"
44-
slog.Warn(errMsg, "labels", newLabels)
44+
slog.Warn(errMsg, "labels", newUserLabels)
4545
return api.PutV2ClustersNameLabels400JSONResponse{
4646
N400BadRequestJSONResponse: api.N400BadRequestJSONResponse{
4747
Message: &errMsg,
@@ -56,7 +56,7 @@ func (s *Server) PutV2ClustersNameLabels(ctx context.Context, request api.PutV2C
5656
return api.PutV2ClustersNameLabels500JSONResponse{N500InternalServerErrorJSONResponse: api.N500InternalServerErrorJSONResponse{Message: &message}}, nil
5757
}
5858

59-
err = cli.CreateClusterLabels(ctx, activeProjectID, clusterName, newLabels)
59+
err = cli.SetClusterLabels(ctx, activeProjectID, clusterName, newUserLabels)
6060

6161
switch {
6262
case errors.IsBadRequest(err):
@@ -73,6 +73,6 @@ func (s *Server) PutV2ClustersNameLabels(ctx context.Context, request api.PutV2C
7373
return api.PutV2ClustersNameLabels500JSONResponse{N500InternalServerErrorJSONResponse: api.N500InternalServerErrorJSONResponse{Message: &message}}, nil
7474
}
7575

76-
slog.Info("Cluster labels updated", "namespace", activeProjectID, "name", request.Name, "labels", newLabels)
76+
slog.Info("Cluster labels updated", "namespace", activeProjectID, "name", request.Name, "labels", newUserLabels)
7777
return api.PutV2ClustersNameLabels200Response{}, nil
7878
}

test/service/service_suite_test.go

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ var _ = Describe("Cluster create/delete flow", Ordered, func() {
203203
params := api.PutV2ClustersNameLabelsParams{}
204204
params.Activeprojectid = testTenantID
205205
body := api.PutV2ClustersNameLabelsJSONRequestBody{
206-
Labels: &map[string]string{"app": "wordpress"},
206+
Labels: &map[string]string{"app": "wordpress", "default-extension": "baseline"},
207207
}
208208
resp, err := cli.PutV2ClustersNameLabelsWithResponse(context.Background(), clusterName, &params, body)
209209
Expect(err).ToNot(HaveOccurred())
@@ -218,10 +218,9 @@ var _ = Describe("Cluster create/delete flow", Ordered, func() {
218218
Expect(err).ToNot(HaveOccurred())
219219
Expect(resp.StatusCode()).To(Equal(200))
220220
Expect(*resp.JSON200.Name).To(Equal(clusterName))
221-
Expect(*resp.JSON200.Labels).To(HaveLen(3))
221+
Expect(*resp.JSON200.Labels).To(HaveLen(2))
222222
Expect(*resp.JSON200.Labels).To(HaveKeyWithValue("app", "wordpress"))
223223
Expect(*resp.JSON200.Labels).To(HaveKeyWithValue("default-extension", "baseline"))
224-
Expect(*resp.JSON200.Labels).To(HaveKeyWithValue("trusted-compute-compatible", "false"))
225224
Expect(*resp.JSON200.Nodes).To(HaveLen(1))
226225
nodes := *resp.JSON200.Nodes
227226
Expect(*nodes[0].Role).To(Equal("all"))
@@ -234,13 +233,35 @@ var _ = Describe("Cluster create/delete flow", Ordered, func() {
234233
err = containsLabels(testTenantID.String(), clusterName, []string{
235234
"app:wordpress",
236235
"default-extension:baseline",
237-
"trusted-compute-compatible:false",
238236
fmt.Sprintf("edge-orchestrator.intel.com/clustername:%v", clusterName),
239237
fmt.Sprintf("edge-orchestrator.intel.com/project-id:%v", testTenantID.String()),
240238
})
241239
Expect(err).ToNot(HaveOccurred())
242240
})
243241

242+
It("Should delete label", func() {
243+
params := api.PutV2ClustersNameLabelsParams{}
244+
params.Activeprojectid = testTenantID
245+
body := api.PutV2ClustersNameLabelsJSONRequestBody{
246+
Labels: &map[string]string{"default-extension": "baseline"},
247+
}
248+
resp, err := cli.PutV2ClustersNameLabelsWithResponse(context.Background(), clusterName, &params, body)
249+
Expect(err).ToNot(HaveOccurred())
250+
Expect(resp.StatusCode()).To(Equal(200))
251+
252+
})
253+
254+
It("Deleted label should be missing in label list", func() {
255+
params := api.GetV2ClustersNameParams{}
256+
params.Activeprojectid = testTenantID
257+
resp, err := cli.GetV2ClustersNameWithResponse(context.Background(), clusterName, &params)
258+
Expect(err).ToNot(HaveOccurred())
259+
Expect(resp.StatusCode()).To(Equal(200))
260+
Expect(*resp.JSON200.Name).To(Equal(clusterName))
261+
Expect(*resp.JSON200.Labels).To(HaveLen(1))
262+
Expect(*resp.JSON200.Labels).To(HaveKeyWithValue("default-extension", "baseline"))
263+
})
264+
244265
It("Should fail to delete cluster template if cluster is running", func() {
245266
params := api.DeleteV2TemplatesNameVersionParams{}
246267
params.Activeprojectid = testTenantID

0 commit comments

Comments
 (0)