Skip to content

feat : Add support to add Service annotations from DevWorkspaceRouting configuration (#1293) #1439

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

rohanKanojia
Copy link
Collaborator

@rohanKanojia rohanKanojia commented May 29, 2025

What does this PR do?

Add support for adding Service annotations from DevWorkspace component configuration to actual Service created by DevWorkspace operator

  • Add new field service that contains annotation field for storing annotations in DevWorkspaceRouting CustomResourceDefinition
  • Update code to read DevWorkspace component service annotation to DevWorkspaceRouting service configuration and apply these annotations while creating routing objects in DevWorkspaceRouting controller
  • Removes references to service annotations from the unsupported Devfile API documentation
  • Remove the webhook warning when applying service annotations

Signed-off-by: Rohan Kumar [email protected]

What issues does this PR fix or reference?

Fix #1293

Is it tested? How?

  • Make sure CRC cluster is running
  • Install DWO on cluster based on changes added in this PR. You'd need to create new container image and install operator
export DWO_IMG=quay.io/rokumar/devworkspace-controller:dev
make docker
make install
  • Apply the following DevWorkspace object, that adds one endpoint exposes by a Service. There is also configuration for adding annotations in Service:
kind: DevWorkspace
apiVersion: workspace.devfile.io/v1alpha2
metadata:
  name: plain-dw-endpoint-annotations
spec:
  started: true
  routingClass: 'basic'
  template:
    components:
      - name: web-terminal
        container:
          image: quay.io/wto/web-terminal-tooling:next
          memoryRequest: 256Mi
          memoryLimit: 512Mi
          mountSources: true
          command:
           - "tail"
           - "-f"
           - "/dev/null"
          annotation:
            service:
              first-service-annotation: test
              second-service-annotation: test2
          endpoints:
          - name: endpoint-1
            targetPort: 8080
            protocol: http
            annotation:
              first-endpoint-annotation: test1
              second-endpoint-annotation: test2  
  • Once the workspace has started, check the created Service, annotations specified in should be applied there
oc get svc workspaceaf104799e6fb4633-service -ojsonpath='{.metadata.annotations}'
{"first-service-annotation":"test","second-service-annotation":"test2"}

PR Checklist

  • E2E tests pass (when PR is ready, comment /test v8-devworkspace-operator-e2e, v8-che-happy-path to trigger)
    • v8-devworkspace-operator-e2e: DevWorkspace e2e test
    • v8-che-happy-path: Happy path for verification integration with Che

Copy link

openshift-ci bot commented May 29, 2025

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@rohanKanojia rohanKanojia force-pushed the pr/issue1293 branch 5 times, most recently from abfb699 to 708d590 Compare May 29, 2025 10:21
@rohanKanojia
Copy link
Collaborator Author

/ok-to-test

@rohanKanojia rohanKanojia force-pushed the pr/issue1293 branch 3 times, most recently from 2c63671 to 96c6a02 Compare May 29, 2025 14:18
@rohanKanojia
Copy link
Collaborator Author

/ok-to-test

@@ -36,9 +36,10 @@ type DevWorkspaceMetadata struct {

// 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(routingSpec controllerv1alpha1.DevWorkspaceRoutingSpec, meta DevWorkspaceMetadata) []corev1.Service {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If possible, it would be best if this function signature (and also GetServiceForEndpoints) are not changed so that function consumers are not affected. For example for che-operator, we use solver.GetServiceForEndpoints in che_routing.go

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, if we want to change the function signature, maybe we can refactor it to use "Functional Options Pattern" so that new fields can be easily added without breaking consumer code: https://golang.cafe/blog/golang-functional-options-pattern.html ?

For example something similar to:

solver.GetDiscoverableServicesForEndpoints(
    solver.WithEndpoints(endpoints),
    solver.WithMetadata(metadata),
    solver.WithAnnotations(annotations),
)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, good point. I wasn't aware about this. Let me think if it's possible to do it without changing method signature.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking closely at the changes, Currently Service generation is closely tied to Endpoints, We're going over component to endpoint map and creating Service accordingly. At this point we know for which component we're creating Service, so we can add appropriate annotations for appropriate component.

However, if we want to move this logic outside and set annotations after creating the Service; it would be uncertain which Service belongs to which component. If we decide to mutate Service with a marker label containing component, it should become possible:

I mean replacing this

Namespace: meta.Namespace,
Labels: map[string]string{
constants.DevWorkspaceIDLabel: meta.DevWorkspaceId,
},

                                                Labels: map[string]string{
                                                        constants.DevWorkspaceIDLabel: meta.DevWorkspaceId,
+                                                       "workspace.dev/component":     componentName,
                                                },

We can then iterate over generated Services and apply annotations by reading component label (that can be deleted after processing). If you think this approach is okay then I can make changes to keep original method signature.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO it should be the DWO that provides the API that conforms with the Devfile spec, therefore, it makes more sense to apply annotations within the functions provided by DWO

Copy link

openshift-ci bot commented Jul 21, 2025

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: dkwon17, rohanKanojia

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@dkwon17 dkwon17 self-requested a review July 21, 2025 20:09
}

var isOpenShift = func() bool {
return infrastructure.IsOpenShift()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since infrastructure.IsOpenShift() is commonly used in the codebase, I don't think this fcuntion is necessary

@@ -43,12 +43,38 @@ var nginxIngressAnnotations = func(endpointName string, endpointAnnotations map[
return annotations
}

func serviceAnnotations(sourceAnnotations map[string]string, isDiscoverable bool, serviceRoutingConfig controllerv1alpha1.Service) map[string]string {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
func serviceAnnotations(sourceAnnotations map[string]string, isDiscoverable bool, serviceRoutingConfig controllerv1alpha1.Service) map[string]string {
func mergeServiceAnnotations(sourceAnnotations map[string]string, serviceRoutingConfig controllerv1alpha1.Service) map[string]string {

I suggest "merge" for more clarity.

Also, for readability and simplicity I think it would be best to avoid boolean flags and instead call the function like:

serviceAnnotations := map[string]string{
  constants.DevWorkspaceDiscoverableServiceAnnotation: "true",
}

mergeServiceAnnotations(serviceAnnotations, routingSpec.Service[componentName]),

WDYT?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add Service Annotations to the DevWorkspaceRouting CRD
2 participants