Repository navigation
Conversation
|
I tested the PR with abovementioned steps and it seems to work as expected ✔️ |
|
/retest |
| // GetDiscoverableServicesForEndpoints converts the endpoint list into a set of services, each corresponding to a single discoverable | ||
| // endpoint from the list. Endpoints with the NoneEndpointExposure are ignored. | ||
| func GetDiscoverableServicesForEndpoints(endpoints map[string]controllerv1alpha1.EndpointList, meta DevWorkspaceMetadata) []corev1.Service { | ||
| func GetDiscoverableServicesForEndpoints(endpoints map[string]controllerv1alpha1.EndpointList, meta DevWorkspaceMetadata, cl client.Client, log logr.Logger) ([]corev1.Service, error) { |
There was a problem hiding this comment.
@akurinnoy is there a reason for implementing duplicate endpiont detection in both the webhook and the GetDiscoverableServicesForEndpoints function?
There was a problem hiding this comment.
While the webhook prevents the DevWorkspaces (which will cause endpoint conflicts) from being created, the controller checks other cases:
- two workspaces created simultaneously - both of them may pass the webhook validation
- services can be created from other sources
- old workspaces, created before this validation
Is this the answer to your question?
There was a problem hiding this comment.
Btw, while looking at this method, I noticed it’s also used in che-operator here:
objs.Services = solvers.GetDiscoverableServicesForEndpoints(routing.Spec.Endpoints, workspaceMeta)Just wanted to give a heads-up that we’ll likely need to update there as well.
|
@akurinnoy : I ran your claude skill The webhook validation has a logic error that causes it to reject valid workspace creations. Currently, if the current workspace has a discoverable endpoint named X, and another workspace has any endpoint (discoverable OR NOT) named X, we reject it. Steps to reproduce
Do you think Workspace B should be allowed because Workspace A's endpoint is NOT discoverable and doesn't create a service named |
|
Hi! I'm che-ai-assistant — I help with your pull requests. I check for new commands every 5m0s (if I am not busy :) ). Available commands:
|
📝 Walkthrough
Pre-merge checks:
|
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@controllers/controller/devworkspacerouting/devworkspacerouting_controller.go`:
- Around line 128-133: The code sets workspaceMeta.DevWorkspaceName =
instance.Name but must use the parent DevWorkspace's name from
instance.OwnerReferences; update the DevWorkspaceMetadata population so
DevWorkspaceName is assigned from the owner reference whose Kind (or
APIVersion+Kind) indicates the parent DevWorkspace (e.g., iterate
instance.OwnerReferences, find ownerRef.Kind == "DevWorkspace" and use
ownerRef.Name), and fall back to instance.Name only if no such ownerRef exists;
change the assignment near workspaceMeta (DevWorkspaceMetadata,
workspaceMeta.DevWorkspaceName) to use that ownerRef-derived name.
In `@controllers/controller/devworkspacerouting/solvers/common_test.go`:
- Around line 16-27: Reorder the import block in common_test.go into three
groups with blank lines between them: (1) standard library (e.g., "testing"),
(2) third-party and Kubernetes packages (e.g.,
"github.com/stretchr/testify/assert", "k8s.io/api/core/v1" as corev1,
"k8s.io/apimachinery/pkg/apis/meta/v1" as metav1,
"k8s.io/apimachinery/pkg/runtime",
"sigs.k8s.io/controller-runtime/pkg/client/fake",
"sigs.k8s.io/controller-runtime/pkg/log/zap"), and (3) project-local imports
(e.g., "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" as
controllerv1alpha1 and
"github.com/devfile/devworkspace-operator/pkg/constants"); insert single blank
lines between these groups and then run 'make fmt' to apply formatting.
In `@webhook/workspace/handler/validate_test.go`:
- Around line 105-124: The test currently asserts a ServiceConflictError when
validateEndpoints (on WebhookHandler) is called with a workspace and a
deletingWorkspace (created via setupWorkspace) that has DeletionTimestamp set;
update the test so it reflects webhook behavior that ignores
deletion-timestamped workspaces: call handler.validateEndpoints(context.TODO(),
workspace) and assert no error (i.e., expect nil) instead of asserting
Error/ErrorAs for *solvers.ServiceConflictError, and remove or replace the
subsequent checks for conflictErr.EndpointName/WorkspaceName accordingly so the
test verifies that deletion-timestamped workspaces do not cause conflicts.
- Around line 16-31: Reorder the import block in the validate_test.go file into
three blank-line-separated groups: (1) standard library imports (context, os,
path/filepath, testing), (2) third-party and Kubernetes imports
(github.com/stretchr/testify/assert, sigs.k8s.io/... , k8s.io/... etc.), and (3)
project-local imports (github.com/devfile/... and your controller packages),
making sure each package stays in its respective group; after rearranging, run
make fmt to apply formatting.
In `@webhook/workspace/handler/validate.go`:
- Around line 81-84: validateEndpoints currently returns raw errors from
h.Client.List which are being appended to devfileErrors and treated as user
validation failures in ValidateDevfile; change validateEndpoints signature to
return (*solvers.ServiceConflictError, error) so real validation conflicts come
back as the typed value and infrastructure/API errors flow via the error return,
update validateEndpoints to return (nil, err) for h.Client.List failures and
(conflictErr, nil) for validation failures, then in ValidateDevfile (and the
other call site around lines 109-112) check the error return first and convert
infrastructure errors into admission.Errored(http.StatusBadRequest, err) (or
propagate the error) instead of appending to devfileErrors, only append
conflictErr.Error() to devfileErrors when the typed conflict value is returned.
- Around line 114-130: The validation loop currently only skips the same
workspace by UID but still inspects workspaces that are being deleted; modify
the loop that iterates workspaceList.Items (and the check around otherWorkspace)
to also skip any otherWorkspace with a non-nil DeletionTimestamp (i.e., treat
terminating workspaces as excluded from discoverable endpoint conflict checks),
so that discoverableEndpoints lookup and potential return of
solvers.ServiceConflictError only considers non-terminating workspaces.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 981c002a-3f92-488e-8077-9057055e9194
📒 Files selected for processing (12)
controllers/controller/devworkspacerouting/devworkspacerouting_controller.gocontrollers/controller/devworkspacerouting/solvers/basic_solver.gocontrollers/controller/devworkspacerouting/solvers/cluster_solver.gocontrollers/controller/devworkspacerouting/solvers/common.gocontrollers/controller/devworkspacerouting/solvers/common_test.gocontrollers/controller/devworkspacerouting/solvers/errors.gocontrollers/controller/devworkspacerouting/solvers/solver.gocontrollers/workspace/suite_test.gopkg/webhook/cluster_roles.gowebhook/workspace/handler/testdata/test-devworkspace.yamlwebhook/workspace/handler/validate.gowebhook/workspace/handler/validate_test.go
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@webhook/workspace/handler/validate_test.go`:
- Line 1: Update the copyright header in
webhook/workspace/handler/validate_test.go by changing the year range string "//
Copyright (c) 2019-2025 Red Hat, Inc." to the current-year range "// Copyright
(c) 2019-2026 Red Hat, Inc."; locate and replace that exact header line at the
top of the file so it matches the required format used across Go sources.
- Line 58: The test currently ignores the error from dwv2.AddToScheme(scheme);
change this to explicitly assert success by calling assert.NoError(t,
dwv2.AddToScheme(scheme)) so the scheme initialization failure surfaces in the
test; locate the call to dwv2.AddToScheme in validate_test.go (the variable
scheme) and replace the `_ =` ignoring pattern with the assert.NoError check
(using the existing testify/assert import).
In `@webhook/workspace/handler/validate.go`:
- Around line 24-29: Reorder the import block in validate.go so imports follow
Go grouping: first any standard-library packages (none shown here), then a blank
line, then third-party and Kubernetes imports together (e.g.,
"github.com/devfile/api/v2/..." packages and
"sigs.k8s.io/controller-runtime/..." client and admission), then a blank line,
and finally project-local imports (e.g.,
"github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" and
"github.com/devfile/devworkspace-operator/controllers/controller/devworkspacerouting/solvers");
ensure there are blank lines separating these three groups and keep the existing
import identifiers (dwv2, devfilevalidation, v1alpha1, solvers, client,
admission) intact.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 13233f4a-6817-4e7f-a346-91250ddebbe0
📒 Files selected for processing (4)
.gitignorecontrollers/controller/devworkspacerouting/solvers/common_test.gowebhook/workspace/handler/validate.gowebhook/workspace/handler/validate_test.go
✅ Files skipped from review due to trivial changes (1)
- .gitignore
🚧 Files skipped from review as they are similar to previous changes (1)
- controllers/controller/devworkspacerouting/solvers/common_test.go
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: akurinnoy, rohanKanojia, tolusha The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
/che-ai-assistant ok-pr-readiness Review is complete. Please check the review comments below. |
PR Readiness Assessment: PR #1521 — fix: identical endpoint name conflictsRepository: devfile/devworkspace-operator
Overall: READY Missing InformationNone — this PR is exceptionally well-documented. What's Good
|
d899dfb to
4d93c08
Compare
|
New changes are detected. LGTM label has been removed. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1521 +/- ##
==========================================
+ Coverage 40.45% 44.68% +4.23%
==========================================
Files 174 179 +5
Lines 15949 13408 -2541
==========================================
- Hits 6452 5992 -460
+ Misses 9096 6963 -2133
- Partials 401 453 +52 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Validate normalized discoverable endpoint names at admission and reconciliation. Use authoritative Service reads and explicit guarded synchronization to preserve foreign ownership and owned Service identity. Keep diagnostics deterministic and cover cache misses, concurrent writes, cleanup and label repair. Signed-off-by: Oleksii Kurinnyi <okurinny@redhat.com> Assisted-by: Codex
Preserve endpoint normalization while reducing intermediate allocations. Retain regression coverage for normalization and generated certificate volume names. Signed-off-by: Oleksii Kurinnyi <okurinny@redhat.com> Assisted-by: Codex
Use a native cache field index for namespace-scoped discoverable endpoint checks, including AdmissionRequest namespace fallback. Preserve update gating, self exclusion, boolean/string attributes and normal object-copy safety. Document RBAC prerequisites and index costs. Signed-off-by: Oleksii Kurinnyi <okurinny@redhat.com> Assisted-by: Codex
4d93c08 to
d036841
Compare
Keep routing pending while service-ca issues discoverable endpoint certificates. Validate Secret identity, ownership, TLS type and required keys before publishing mounts; preserve foreign Secrets and omit unrelated Secret data. Signed-off-by: Oleksii Kurinnyi <okurinny@redhat.com> Assisted-by: Codex
Record the observed routing generation and retry failed routing after a spec correction. Keep failures terminal within one generation, preserve foreign Services, and install the optional observedGeneration field in all CRD artifacts. Signed-off-by: Oleksii Kurinnyi <okurinny@redhat.com> Assisted-by: Codex
Pass reconciliation context through routing solvers, Service lookup, synchronization and cleanup. Propagate cancellation without an additional status write or explicit requeue. Cover both built-in solvers and the shared context contract. Signed-off-by: Oleksii Kurinnyi <okurinny@redhat.com> Assisted-by: Codex
d036841 to
413b56f
Compare
What does this PR do?
Prevents discoverable endpoints from overwriting Services owned by another workspace, including endpoints whose names normalize to the same Service name.
status.observedGenerationfield. Failures remain terminal within a generation. Install updated CRDs before the updated controller so this field is retained.RoutingSolverimplementations must update the context-bearingGetSpecObjectssignature.RBAC: the webhook ServiceAccount gains cluster-wide DevWorkspace
get/list/watchthrough its existing ClusterRoleBinding. This permits reading full workspace specs, including literal environment values and repository URLs, across namespaces. Conflict decisions remain namespace-scoped. The index reuses these permissions and adds no further RBAC.The ADRs record ownership, certificate naming/readiness, indexing, generation recovery and context decisions. Load-test tooling and detailed campaign reports are preserved on a local branch for a later separate PR. No load-testing files are included in this PR.
What issues does this PR fix or reference?
Fixes eclipse-che/che#23231
Separate follow-ups: the backup CronJob nil-Workspace configuration panic predates this PR; its production fix is out of scope and the e2e workaround is documented. A stricter public endpoint-alias case reproduced an existing normalized Route/raw-name URL lookup mismatch. Its expected behavior needs team discussion. Neither issue has been filed yet, and neither production fix is included here.
Is it tested? How?
Use a cluster running this PR's controller and webhook image, with its updated CRDs installed first. The steps below use
ocand disposable namespaces; certificate checks require OpenShift service-ca. Run the command blocks in the same shell. Commands marked as expected denials should fail admission.Same-namespace conflicts and normalized names
Save this as
workspace.yaml:Create the first workspace and wait for readiness before trying a second:
Expected: the second create is rejected with
discoverable endpoint 'postgresql-db' is already in use by workspace 'endpoint-test-1'. Repeat with a differently spelled endpoint that normalizes to the same Service name:Expected: the same conflict denial; neither attempt creates
endpoint-test-2.Namespace isolation and discoverability updates
The same endpoint must work in another namespace:
Create a stopped, non-discoverable workspace in the first namespace, then try enabling discoverability with both encodings:
Expected: creation succeeds; both updates are denied with the same conflict reason. The stopped workspace creates no workspace pod.
An unchanged endpoint-name set and a discoverability removal must remain admissible. These probes use server-side dry-run to preserve the first workspace:
Expected: both succeed without persisting changes.
Foreign Service protection and routing recovery
Use a standalone routing object to exercise reconciliation independently of admission and workspace startup. Create a foreign Service and save its contents:
Expected: routing fails with an ownership conflict and
status.observedGenerationmatchesmetadata.generation. Rename the endpoint on the same routing object:Expected: the routing keeps its UID, advances generation, reaches Ready with matching observed generation, and creates
review-recovered. The foreign Service remains identical. Cluster routing creates no public Route/Ingress.Per-Service certificates and foreign Secrets (OpenShift)
Switch the recovered routing to TLS:
Expected: after service-ca issues certificates,
status.podAdditionscontains the aggregate mount at/var/serving-cert/and a distinct discoverable mount at/var/serving-cert/review-recovered/. Each certificate Secret's originating Service name/UID annotations and Service owner reference match its Service. Volumes project onlytls.crtandtls.key.Check that an unrelated Secret cannot become a workspace mount:
Expected: routing fails certificate validation, publishes no Pod additions, and leaves the Secret unchanged.
Cleanup
Delete both namespaces and wait until namespace finalization removes all workspace, routing, Service and storage fixtures:
Expected: both namespaces are NotFound. This fixture requests ephemeral workspace storage.
PR Checklist
Local e2e results are separate from the named upstream CI jobs.
v8-devworkspace-operator-e2eresult — pending review-time CI.v8-che-happy-path/ Che integration verification — explicitly deferred.