kubeslice / kubeslice/kubeslice-controller

Bug: ReconcileProject overwrites all Project labels on every reconciliation cycle

Open
#318 0 comments 0 reactions 1 assignee Claimed by @richiesebastian View on GitHub
bug
Dominant language
Go
Stars
73
Forks
48
Avg merge
2d 21h
Merged PRs (30d)
8

Description

### 📜 Description

`ReconcileProject` in `service/project_service.go` destroys all existing Project labels on every reconciliation cycle.

At lines 117–131, the function carefully merges ConfigMap-sourced labels and the `LabelProjectNamespace` key into the existing `project.Labels` map and persists it via `util.UpdateResource`.

Then at lines 169–173, it creates a brand-new single-key map, assigns it to `project.Labels` replacing the entire map and calls `util.UpdateResource`, wiping every other label:

```go
// service/project_service.go, lines 169–173
labels := make(map[string]string)
labels["kubeslice-project-namespace"] = projectNamespace
project.Labels = labels

err = util.UpdateResource(ctx, project)
```

### 👟 Reproduction steps

1. Create a Project with user-defined labels:

apiVersion: controller.kubeslice.io/v1alpha1
kind: Project
metadata:
name: my-project
namespace: kubeslice-controller
labels:
team: platform
environment: staging
spec:
serviceAccount:
readWrite:
- admin-user

2. Wait for the `ProjectReconciler` to complete one reconciliation cycle (~seconds).

3. Read back the Project labels:

kubectl get project my-project -n kubeslice-controller -o jsonpath='{.metadata.labels}'

4. Observe that `team` and `environment` labels are gone.

### 👍 Expected behavior

Labels should contain all original user-set keys plus the controller-managed key:

{
"team": "platform",
"environment": "staging",
"kubeslice-project-namespace": "kubeslice-my-project"
}

The controller should upsert `kubeslice-project-namespace` into the existing map without removing any other labels.

### 👎 Actual Behavior

Labels contain only the single controller-managed key all user-defined labels are silently deleted after the first reconciliation cycle:

{
"kubeslice-project-namespace": "kubeslice-my-project"
}

Manually re-adding the missing labels is immediately undone on the next reconciliation loop. The wipe happens on every cycle without any error or warning.

### 🐚 Relevant log output

```shell
The label overwrite produces no log output at the service level. Lines 168–173 of ReconcileProject have no logger call, and util.UpdateResource only emits output on failure.

A successful reconciliation looks like this regardless of how many labels were wiped:

INFO Starting Recoincilation of Project with name my-project in namespace kubeslice-controller
INFO project my-project reconciled

To observe the label destruction you must enable Kubernetes API audit logging and look for
PATCH events on the Project resource, or watch the resource directly:

kubectl get project my-project -n kubeslice-controller -w -o jsonpath='{.metadata.labels}'

The absence of any warning is itself notable. The label wipe is completely silent by design,
which makes this bug difficult to detect in production without external monitoring.
```

### Version

master branch (latest HEAD as of 2026-05-07). The bug exists in all releases containing the label-overwrite logic at lines 169–171 of ReconcileProject. Introduced alongside the default slice feature. Affected release branches include release-shimla, release-udaipur, and release-varanasi.

### 🖥️ What operating system are you seeing the problem on?

Windows

### ✅ Proposed Solution

Upsert into the existing map instead of replacing it at service/project_service.go lines 169 - 171.

Current code (lines 169–171):

labels := make(map[string]string)
labels["kubeslice-project-namespace"] = projectNamespace
project.Labels = labels

Fixed code:

if project.Labels == nil {
project.Labels = make(map[string]string)
}
project.Labels["kubeslice-project-namespace"] = projectNamespace

This is consistent with how the same function already handles labels earlier at lines 117–122, where it nil-checks project.Labels first and then sets individual keys. The fix simply applies the same pattern to the later label assignment.

### 👀 Have you spent some time to check if this issue has been raised before?

- [x] I checked and didn't find any similar issue

### Code of Conduct

- [x] I agree to follow this project's Code of Conduct

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.