Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions api/v1alpha1/gitopsservice_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ type GitopsServiceSpec struct {
Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
// NodeSelector is a map of key value pairs used for node selection in the default workloads
NodeSelector map[string]string `json:"nodeSelector,omitempty"`

// Resources defines the Compute Resources required by the console-plugin and gitops-backend pods.
Resources *corev1.ResourceRequirements `json:"resources,omitempty"`
}

// GitopsServiceStatus defines the observed state of GitopsService
Expand Down
5 changes: 5 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ metadata:
capabilities: Deep Insights
console.openshift.io/plugins: '["gitops-plugin"]'
containerImage: quay.io/redhat-developer/gitops-operator
createdAt: "2025-08-21T01:20:45Z"
createdAt: "2025-09-16T07:12:34Z"
description: Enables teams to adopt GitOps principles for managing cluster configurations
and application delivery across hybrid multi-cluster Kubernetes environments.
features.operators.openshift.io/disconnected: "true"
Expand Down
60 changes: 60 additions & 0 deletions bundle/manifests/pipelines.openshift.io_gitopsservices.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,66 @@ spec:
description: NodeSelector is a map of key value pairs used for node
selection in the default workloads
type: object
resources:
description: Resources defines the Compute Resources required by the
console-plugin and gitops-backend pods.
properties:
claims:
description: |-
Claims lists the names of resources, defined in spec.resourceClaims,
that are used by this container.

This is an alpha field and requires enabling the
DynamicResourceAllocation feature gate.

This field is immutable. It can only be set for containers.
items:
description: ResourceClaim references one entry in PodSpec.ResourceClaims.
properties:
name:
description: |-
Name must match the name of one entry in pod.spec.resourceClaims of
the Pod where this field is used. It makes that resource available
inside a container.
type: string
request:
description: |-
Request is the name chosen for a request in the referenced claim.
If empty, everything from the claim is made available, otherwise
only the result of this request.
type: string
required:
- name
type: object
type: array
x-kubernetes-list-map-keys:
- name
x-kubernetes-list-type: map
limits:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
description: |-
Limits describes the maximum amount of compute resources allowed.
More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
type: object
requests:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
description: |-
Requests describes the minimum amount of compute resources required.
If Requests is omitted for a container, it defaults to Limits if that is explicitly specified,
otherwise to an implementation-defined value. Requests cannot exceed Limits.
More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
type: object
type: object
runOnInfra:
description: InfraNodeEnabled will add infra NodeSelector to all the
default workloads of gitops operator
Expand Down
60 changes: 60 additions & 0 deletions config/crd/bases/pipelines.openshift.io_gitopsservices.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,66 @@ spec:
description: NodeSelector is a map of key value pairs used for node
selection in the default workloads
type: object
resources:
description: Resources defines the Compute Resources required by the
console-plugin and gitops-backend pods.
properties:
claims:
description: |-
Claims lists the names of resources, defined in spec.resourceClaims,
that are used by this container.

This is an alpha field and requires enabling the
DynamicResourceAllocation feature gate.

This field is immutable. It can only be set for containers.
items:
description: ResourceClaim references one entry in PodSpec.ResourceClaims.
properties:
name:
description: |-
Name must match the name of one entry in pod.spec.resourceClaims of
the Pod where this field is used. It makes that resource available
inside a container.
type: string
request:
description: |-
Request is the name chosen for a request in the referenced claim.
If empty, everything from the claim is made available, otherwise
only the result of this request.
type: string
required:
- name
type: object
type: array
x-kubernetes-list-map-keys:
- name
x-kubernetes-list-type: map
limits:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
description: |-
Limits describes the maximum amount of compute resources allowed.
More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
type: object
requests:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
description: |-
Requests describes the minimum amount of compute resources required.
If Requests is omitted for a container, it defaults to Limits if that is explicitly specified,
otherwise to an implementation-defined value. Requests cannot exceed Limits.
More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
type: object
type: object
runOnInfra:
description: InfraNodeEnabled will add infra NodeSelector to all the
default workloads of gitops operator
Expand Down
4 changes: 4 additions & 0 deletions controllers/consoleplugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,10 @@ func (r *ReconcileGitopsService) reconcileDeployment(cr *pipelinesv1alpha1.Gitop
newPluginDeployment.Spec.Template.Spec.Tolerations = cr.Spec.Tolerations
}

if cr.Spec.Resources != nil {
newPluginDeployment.Spec.Template.Spec.Resources = cr.Spec.Resources
}

// Check if this Deployment already exists
existingPluginDeployment := &appsv1.Deployment{}

Expand Down
5 changes: 5 additions & 0 deletions controllers/gitopsservice_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,11 @@ func (r *ReconcileGitopsService) reconcileBackend(gitopsserviceNamespacedName ty
if len(instance.Spec.Tolerations) > 0 {
deploymentObj.Spec.Template.Spec.Tolerations = instance.Spec.Tolerations
}

if instance.Spec.Resources != nil {
deploymentObj.Spec.Template.Spec.Resources = instance.Spec.Resources
}

// Check if this Deployment already exists
found := &appsv1.Deployment{}
if err := r.Client.Get(context.TODO(), types.NamespacedName{Name: deploymentObj.Name, Namespace: deploymentObj.Namespace},
Expand Down
46 changes: 46 additions & 0 deletions controllers/gitopsservice_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,52 @@ func TestReconcile_PSSLabels(t *testing.T) {
}
}

func TestReconcile_Resources(t *testing.T) {
logf.SetLogger(argocd.ZapLogger(true))
s := scheme.Scheme
addKnownTypesToScheme(s)
gitopsService := &pipelinesv1alpha1.GitopsService{
ObjectMeta: v1.ObjectMeta{
Name: serviceName,
},
Spec: pipelinesv1alpha1.GitopsServiceSpec{
RunOnInfra: true,
Tolerations: deploymentDefaultTolerations(),
Resources: &corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resourcev1.MustParse("100m"),
corev1.ResourceMemory: resourcev1.MustParse("64Mi"),
},
Limits: corev1.ResourceList{
corev1.ResourceCPU: resourcev1.MustParse("200m"),
corev1.ResourceMemory: resourcev1.MustParse("128Mi"),
},
},
},
}
fakeClient := fake.NewFakeClient(gitopsService)
reconciler := newReconcileGitOpsService(fakeClient, s)

_, err := reconciler.Reconcile(context.TODO(), newRequest("test", "test"))
assertNoError(t, err)

deployment := appsv1.Deployment{}
err = fakeClient.Get(context.TODO(), types.NamespacedName{Name: serviceName, Namespace: serviceNamespace}, &deployment)
assertNoError(t, err)
nSelector := common.InfraNodeSelector()
argoutil.AppendStringMap(nSelector, argocommon.DefaultNodeSelector())
assert.DeepEqual(t, deployment.Spec.Template.Spec.NodeSelector, nSelector)
assert.DeepEqual(t, deployment.Spec.Template.Spec.Tolerations, deploymentDefaultTolerations())
assert.DeepEqual(t, deployment.Spec.Template.Spec.Resources.Requests, corev1.ResourceList{
corev1.ResourceCPU: resourcev1.MustParse("100m"),
corev1.ResourceMemory: resourcev1.MustParse("64Mi"),
})
assert.DeepEqual(t, deployment.Spec.Template.Spec.Resources.Limits, corev1.ResourceList{
corev1.ResourceCPU: resourcev1.MustParse("200m"),
corev1.ResourceMemory: resourcev1.MustParse("128Mi"),
})
}

func addKnownTypesToScheme(scheme *runtime.Scheme) {
scheme.AddKnownTypes(configv1.GroupVersion, &configv1.ClusterVersion{})
scheme.AddKnownTypes(pipelinesv1alpha1.GroupVersion, &pipelinesv1alpha1.GitopsService{})
Expand Down
8 changes: 8 additions & 0 deletions test/openshift/e2e/ginkgo/fixture/deployment/fixture.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,14 @@ func HaveServiceAccountName(expectedServiceAccountName string) matcher.GomegaMat
})
}

// HaveResourceRequirements validates if the deployment object contains the given resource requirements.
func HaveResourceRequirements(requirements *corev1.ResourceRequirements) matcher.GomegaMatcher {
return fetchDeployment(func(depl *appsv1.Deployment) bool {
GinkgoWriter.Println("Deployment HaveResourceRequirements:", "expected: ", requirements.String(), "actual: ", depl.Spec.Template.Spec.Resources.String())
return reflect.DeepEqual(requirements, depl.Spec.Template.Spec.Resources)
})
}

// This is intentionally NOT exported, for now. Create another function in this file/package that calls this function, and export that.
func fetchDeployment(f func(*appsv1.Deployment) bool) matcher.GomegaMatcher {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package parallel

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
gitopsoperatorv1alpha1 "github.com/redhat-developer/gitops-operator/api/v1alpha1"
"github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture"
deploymentFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/deployment"
gitopsserviceFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/gitopsservice"
k8sFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/k8s"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var _ = Describe("GitOps Operator Parallel E2E Tests", func() {

Context("1-120-validate_resource_constraints_gitopsservice_test", func() {
BeforeEach(func() {
fixture.EnsureSequentialCleanSlate()

})

It("validates that GitOpsService can take in custom resource constraints", func() {

By("enabling resource constraints on GitOpsService CR")
gitopsService := &gitopsoperatorv1alpha1.GitopsService{
ObjectMeta: metav1.ObjectMeta{Name: "cluster"},
}
Expect(gitopsService).To(k8sFixture.ExistByName())
gitopsserviceFixture.Update(gitopsService, func(gs *gitopsoperatorv1alpha1.GitopsService) {
gs.Spec.Resources = &corev1.ResourceRequirements{
Requests: corev1.ResourceList{
"cpu": resource.MustParse("200m"),
"memory": resource.MustParse("256Mi"),
},
Limits: corev1.ResourceList{
"cpu": resource.MustParse("500m"),
"memory": resource.MustParse("512Mi"),
},
}
})

// Ensure the change is reverted when the test exits
defer func() {
gitopsserviceFixture.Update(gitopsService, func(gs *gitopsoperatorv1alpha1.GitopsService) {
gs.Spec.Resources = nil
})
}()

By("verifying the openshift-gitops resources have honoured the resource constraints")
clusterDepl := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "cluster", Namespace: "openshift-gitops"}}
Eventually(clusterDepl).Should(
And(
deploymentFixture.HaveResourceRequirements(&corev1.ResourceRequirements{
Requests: corev1.ResourceList{
"cpu": resource.MustParse("200m"),
"memory": resource.MustParse("256Mi"),
},
Limits: corev1.ResourceList{
"cpu": resource.MustParse("500m"),
"memory": resource.MustParse("512Mi"),
},
}),
),
)
})
})
})