diff --git a/bundle/manifests/argocd-image-updater.argoproj.io_imageupdaters.yaml b/bundle/manifests/argocd-image-updater.argoproj.io_imageupdaters.yaml new file mode 100644 index 000000000..47ac2e2d0 --- /dev/null +++ b/bundle/manifests/argocd-image-updater.argoproj.io_imageupdaters.yaml @@ -0,0 +1,556 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.19.0 + creationTimestamp: null + name: imageupdaters.argocd-image-updater.argoproj.io +spec: + group: argocd-image-updater.argoproj.io + names: + kind: ImageUpdater + listKind: ImageUpdaterList + plural: imageupdaters + singular: imageupdater + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: ImageUpdater is the Schema for the imageupdaters API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + ImageUpdaterSpec defines the desired state of ImageUpdater + It specifies which applications to target, default update strategies, + and a list of images to manage. + properties: + applicationRefs: + description: |- + ApplicationRefs indicates the set of applications to be managed. + ApplicationRefs is a list of rules to select Argo CD Applications within the ImageUpdater CR's namespace. + Each reference can also provide specific overrides for the global settings defined above. + items: + description: ApplicationRef contains various criteria by which to + include applications for managing by image updater + properties: + commonUpdateSettings: + description: |- + CommonUpdateSettings overrides the global CommonUpdateSettings for applications + matched by this selector. + This field is ignored when UseAnnotations is true. + properties: + allowTags: + description: |- + AllowTags is a regex pattern for tags to allow. + This acts as the default if not overridden. + type: string + forceUpdate: + default: false + description: |- + ForceUpdate specifies whether updates should be forced. + This acts as the default if not overridden. + type: boolean + ignoreTags: + description: |- + IgnoreTags is a list of glob-like patterns of tags to ignore. + This acts as the default and can be overridden at more specific levels. + items: + type: string + type: array + x-kubernetes-list-type: atomic + platforms: + description: |- + Platforms specifies a list of target platforms (e.g., "linux/amd64", "linux/arm64"). + If specified, the image updater will consider these platforms when checking for new versions or digests. + items: + type: string + type: array + x-kubernetes-list-type: atomic + pullSecret: + description: |- + PullSecret is the pull secret to use for images. + This acts as the default if not overridden. + type: string + updateStrategy: + default: semver + description: |- + UpdateStrategy defines the update strategy to apply. + Examples: "semver", "latest", "digest", "name". + This acts as the default if not overridden at a more specific level. + type: string + type: object + images: + description: |- + Images contains a list of configurations that how images should be updated. + These rules apply to applications selected by namePattern in ApplicationRefs, and each + image can override global/ApplicationRef settings. + This field is ignored when UseAnnotations is true. + items: + description: |- + ImageConfig defines how a specific container image should be discovered, updated, + and how those updates should be reflected in application manifests. + properties: + alias: + description: |- + Alias is a short, user-defined name for this image configuration. + It MUST be unique within a single ApplicationRef's list of images. + This field is mandatory. + pattern: ^[a-zA-Z0-9][a-zA-Z0-9-._]*$ + type: string + commonUpdateSettings: + description: CommonUpdateSettings overrides the effective + default CommonUpdateSettings for this specific image. + properties: + allowTags: + description: |- + AllowTags is a regex pattern for tags to allow. + This acts as the default if not overridden. + type: string + forceUpdate: + default: false + description: |- + ForceUpdate specifies whether updates should be forced. + This acts as the default if not overridden. + type: boolean + ignoreTags: + description: |- + IgnoreTags is a list of glob-like patterns of tags to ignore. + This acts as the default and can be overridden at more specific levels. + items: + type: string + type: array + x-kubernetes-list-type: atomic + platforms: + description: |- + Platforms specifies a list of target platforms (e.g., "linux/amd64", "linux/arm64"). + If specified, the image updater will consider these platforms when checking for new versions or digests. + items: + type: string + type: array + x-kubernetes-list-type: atomic + pullSecret: + description: |- + PullSecret is the pull secret to use for images. + This acts as the default if not overridden. + type: string + updateStrategy: + default: semver + description: |- + UpdateStrategy defines the update strategy to apply. + Examples: "semver", "latest", "digest", "name". + This acts as the default if not overridden at a more specific level. + type: string + type: object + imageName: + description: |- + ImageName is the full identifier of the image to be tracked, + including the registry (if not Docker Hub), the image name, and an initial/current tag or version. + This is the string used to query the container registry and also as a base for finding updates. + Example: "docker.io/library/nginx:1.17.10", "quay.io/prometheus/node-exporter:v1.5.0". + This field is mandatory. + type: string + manifestTargets: + description: |- + ManifestTarget defines how and where to update this image in Kubernetes manifests. + Only one of Helm or Kustomize should be specified within this block. + This whole block is optional if the image update isn't written to a manifest in a structured way. + properties: + helm: + description: |- + Helm specifies update parameters if the target manifest is managed by Helm + and updates are to be made to Helm values files. + properties: + name: + description: |- + Name is the dot-separated path to the Helm key for the image repository/name part. + Example: "image.repository", "frontend.deployment.image.name". + If neither spec nor name/tag are set, defaults to "image.name". + If spec is set, this field is ignored. + type: string + spec: + description: |- + Spec is the dot-separated path to a Helm key where the full image string + (e.g., "image/name:1.0") should be written. + Use this if your Helm chart expects the entire image reference in a single field, + rather than separate name/tag fields. If this is set, name and tag will be ignored. + type: string + tag: + description: |- + Tag is the dot-separated path to the Helm key for the image tag part. + Example: "image.tag", "frontend.deployment.image.version". + If neither spec nor name/tag are set, defaults to "image.tag". + If spec is set, this field is ignored. + type: string + type: object + kustomize: + description: |- + Kustomize specifies update parameters if the target manifest is managed by Kustomize + and updates involve changing image tags in Kustomize configurations. + properties: + name: + description: |- + Name is the image name (which can include the registry and an initial tag) + as it appears in the `images` list of a kustomization.yaml file that needs to be updated. + The updater will typically change the tag or add a digest to this entry. + Example: "docker.io/library/nginx". + This field is required if the Kustomize target is used. + type: string + required: + - name + type: object + type: object + x-kubernetes-validations: + - message: Exactly one of helm or kustomize must be specified + within manifestTargets if the block is present. + rule: 'has(self.helm) ? !has(self.kustomize) : has(self.kustomize)' + required: + - alias + - imageName + type: object + type: array + x-kubernetes-list-map-keys: + - alias + x-kubernetes-list-type: map + labelSelectors: + description: LabelSelectors indicates the label selectors to + apply for application selection + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namePattern: + description: NamePattern indicates the glob pattern for application + name + type: string + useAnnotations: + default: false + description: |- + UseAnnotations When true, read image configuration from Application's + argocd-image-updater.argoproj.io/* annotations instead of + requiring explicit Images[] configuration in this CR. + When this field is set to true, only namePattern and labelSelectors are used for + application selection. All other fields (CommonUpdateSettings, WriteBackConfig, Images) + are ignored. + type: boolean + writeBackConfig: + description: |- + WriteBackConfig overrides the global WriteBackConfig settings for applications + matched by this selector. + This field is ignored when UseAnnotations is true. + properties: + gitConfig: + description: |- + GitConfig provides Git configuration settings if the write-back method involves Git. + This can only be used when method is "git" or starts with "git:". + properties: + branch: + description: |- + Branch to commit updates to. + Required if write-back method is Git and this is not specified at the spec level. + type: string + repository: + description: |- + Repository URL to commit changes to. + If not specified here or at the spec level, the controller MUST infer it from the + Argo CD Application's `spec.source.repoURL`. This field allows overriding that. + type: string + writeBackTarget: + description: |- + WriteBackTarget defines the path and type of file to update in the Git repository. + Examples: "helmvalues:./helm/values.yaml", "kustomization:./kustomize/overlays/production". + For ApplicationSet usage, `{{ .app.path.path }}` should be resolved by ApplicationSet + before this CR is generated, resulting in a concrete path here. + Required if write-back method is Git and this is not specified at the spec level. + type: string + type: object + method: + default: argocd + description: |- + Method defines the method for writing back updated image versions. + This acts as the default if not overridden. If not specified, defaults to "argocd". + pattern: ^(argocd|git|git:[a-zA-Z0-9][a-zA-Z0-9-._/:]*)$ + type: string + required: + - method + type: object + required: + - namePattern + type: object + x-kubernetes-validations: + - message: Either useAnnotations must be true, or images must be + provided with at least one item + rule: '!(has(self.useAnnotations) && self.useAnnotations == true) + ? (has(self.images) && size(self.images) > 0) : true' + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - namePattern + x-kubernetes-list-type: map + commonUpdateSettings: + description: |- + CommonUpdateSettings provides global default settings for update strategies, + tag filtering, pull secrets, etc., for all applications matched by this CR. + These can be overridden at the ApplicationRef or ImageConfig level. + properties: + allowTags: + description: |- + AllowTags is a regex pattern for tags to allow. + This acts as the default if not overridden. + type: string + forceUpdate: + default: false + description: |- + ForceUpdate specifies whether updates should be forced. + This acts as the default if not overridden. + type: boolean + ignoreTags: + description: |- + IgnoreTags is a list of glob-like patterns of tags to ignore. + This acts as the default and can be overridden at more specific levels. + items: + type: string + type: array + x-kubernetes-list-type: atomic + platforms: + description: |- + Platforms specifies a list of target platforms (e.g., "linux/amd64", "linux/arm64"). + If specified, the image updater will consider these platforms when checking for new versions or digests. + items: + type: string + type: array + x-kubernetes-list-type: atomic + pullSecret: + description: |- + PullSecret is the pull secret to use for images. + This acts as the default if not overridden. + type: string + updateStrategy: + default: semver + description: |- + UpdateStrategy defines the update strategy to apply. + Examples: "semver", "latest", "digest", "name". + This acts as the default if not overridden at a more specific level. + type: string + type: object + namespace: + description: |- + Namespace indicates the target namespace of the applications. + + Deprecated: This field is deprecated and will be removed in a future release. + The controller now uses the ImageUpdater CR's namespace (metadata.namespace) + to determine which namespace to search for applications. This field is ignored. + type: string + writeBackConfig: + description: |- + WriteBackConfig provides global default settings for how and where to write back image updates. + This can be overridden at the ApplicationRef level. + properties: + gitConfig: + description: |- + GitConfig provides Git configuration settings if the write-back method involves Git. + This can only be used when method is "git" or starts with "git:". + properties: + branch: + description: |- + Branch to commit updates to. + Required if write-back method is Git and this is not specified at the spec level. + type: string + repository: + description: |- + Repository URL to commit changes to. + If not specified here or at the spec level, the controller MUST infer it from the + Argo CD Application's `spec.source.repoURL`. This field allows overriding that. + type: string + writeBackTarget: + description: |- + WriteBackTarget defines the path and type of file to update in the Git repository. + Examples: "helmvalues:./helm/values.yaml", "kustomization:./kustomize/overlays/production". + For ApplicationSet usage, `{{ .app.path.path }}` should be resolved by ApplicationSet + before this CR is generated, resulting in a concrete path here. + Required if write-back method is Git and this is not specified at the spec level. + type: string + type: object + method: + default: argocd + description: |- + Method defines the method for writing back updated image versions. + This acts as the default if not overridden. If not specified, defaults to "argocd". + pattern: ^(argocd|git|git:[a-zA-Z0-9][a-zA-Z0-9-._/:]*)$ + type: string + required: + - method + type: object + required: + - applicationRefs + type: object + status: + description: ImageUpdaterStatus defines the observed state of ImageUpdater + properties: + conditions: + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + imageStatus: + description: ImageStatus indicates the detailed status for the list + of managed images + items: + description: ImageStatus contains information for an image:version + and its update status in hosting applications + properties: + applications: + description: Applications contains a list of applications and + when the image was last updated therein + items: + description: ImageApplicationLastUpdated contains information + for an application and when the image was last updated therein + properties: + appName: + description: AppName indicates and namespace and the application + name + type: string + lastUpdatedAt: + description: LastUpdatedAt indicates when the image in + this application was last updated + format: date-time + type: string + required: + - appName + type: object + type: array + name: + description: Name indicates the image name + type: string + version: + description: Version indicates the image version + type: string + required: + - name + - version + type: object + type: array + reconciledAt: + description: LastUpdatedAt indicates when the image updater last ran + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/bundle/manifests/gitops-operator.clusterserviceversion.yaml b/bundle/manifests/gitops-operator.clusterserviceversion.yaml index fd4cfa2a1..a8ec05301 100644 --- a/bundle/manifests/gitops-operator.clusterserviceversion.yaml +++ b/bundle/manifests/gitops-operator.clusterserviceversion.yaml @@ -180,7 +180,7 @@ metadata: capabilities: Deep Insights console.openshift.io/plugins: '["gitops-plugin"]' containerImage: quay.io/redhat-developer/gitops-operator - createdAt: "2026-02-18T10:47:28Z" + createdAt: "2026-02-25T06:55:11Z" 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" @@ -306,6 +306,11 @@ spec: kind: GitopsService name: gitopsservices.pipelines.openshift.io version: v1alpha1 + - description: ImageUpdater is the Schema for the imageupdaters API + displayName: ImageUpdater + kind: ImageUpdater + name: imageupdaters.argocd-image-updater.argoproj.io + version: v1alpha1 - kind: NamespaceManagement name: namespacemanagements.argoproj.io version: v1beta1 diff --git a/config/crd/bases/argocd-image-updater.argoproj.io_imageupdaters.yaml b/config/crd/bases/argocd-image-updater.argoproj.io_imageupdaters.yaml new file mode 100644 index 000000000..47ac2e2d0 --- /dev/null +++ b/config/crd/bases/argocd-image-updater.argoproj.io_imageupdaters.yaml @@ -0,0 +1,556 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.19.0 + creationTimestamp: null + name: imageupdaters.argocd-image-updater.argoproj.io +spec: + group: argocd-image-updater.argoproj.io + names: + kind: ImageUpdater + listKind: ImageUpdaterList + plural: imageupdaters + singular: imageupdater + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: ImageUpdater is the Schema for the imageupdaters API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + ImageUpdaterSpec defines the desired state of ImageUpdater + It specifies which applications to target, default update strategies, + and a list of images to manage. + properties: + applicationRefs: + description: |- + ApplicationRefs indicates the set of applications to be managed. + ApplicationRefs is a list of rules to select Argo CD Applications within the ImageUpdater CR's namespace. + Each reference can also provide specific overrides for the global settings defined above. + items: + description: ApplicationRef contains various criteria by which to + include applications for managing by image updater + properties: + commonUpdateSettings: + description: |- + CommonUpdateSettings overrides the global CommonUpdateSettings for applications + matched by this selector. + This field is ignored when UseAnnotations is true. + properties: + allowTags: + description: |- + AllowTags is a regex pattern for tags to allow. + This acts as the default if not overridden. + type: string + forceUpdate: + default: false + description: |- + ForceUpdate specifies whether updates should be forced. + This acts as the default if not overridden. + type: boolean + ignoreTags: + description: |- + IgnoreTags is a list of glob-like patterns of tags to ignore. + This acts as the default and can be overridden at more specific levels. + items: + type: string + type: array + x-kubernetes-list-type: atomic + platforms: + description: |- + Platforms specifies a list of target platforms (e.g., "linux/amd64", "linux/arm64"). + If specified, the image updater will consider these platforms when checking for new versions or digests. + items: + type: string + type: array + x-kubernetes-list-type: atomic + pullSecret: + description: |- + PullSecret is the pull secret to use for images. + This acts as the default if not overridden. + type: string + updateStrategy: + default: semver + description: |- + UpdateStrategy defines the update strategy to apply. + Examples: "semver", "latest", "digest", "name". + This acts as the default if not overridden at a more specific level. + type: string + type: object + images: + description: |- + Images contains a list of configurations that how images should be updated. + These rules apply to applications selected by namePattern in ApplicationRefs, and each + image can override global/ApplicationRef settings. + This field is ignored when UseAnnotations is true. + items: + description: |- + ImageConfig defines how a specific container image should be discovered, updated, + and how those updates should be reflected in application manifests. + properties: + alias: + description: |- + Alias is a short, user-defined name for this image configuration. + It MUST be unique within a single ApplicationRef's list of images. + This field is mandatory. + pattern: ^[a-zA-Z0-9][a-zA-Z0-9-._]*$ + type: string + commonUpdateSettings: + description: CommonUpdateSettings overrides the effective + default CommonUpdateSettings for this specific image. + properties: + allowTags: + description: |- + AllowTags is a regex pattern for tags to allow. + This acts as the default if not overridden. + type: string + forceUpdate: + default: false + description: |- + ForceUpdate specifies whether updates should be forced. + This acts as the default if not overridden. + type: boolean + ignoreTags: + description: |- + IgnoreTags is a list of glob-like patterns of tags to ignore. + This acts as the default and can be overridden at more specific levels. + items: + type: string + type: array + x-kubernetes-list-type: atomic + platforms: + description: |- + Platforms specifies a list of target platforms (e.g., "linux/amd64", "linux/arm64"). + If specified, the image updater will consider these platforms when checking for new versions or digests. + items: + type: string + type: array + x-kubernetes-list-type: atomic + pullSecret: + description: |- + PullSecret is the pull secret to use for images. + This acts as the default if not overridden. + type: string + updateStrategy: + default: semver + description: |- + UpdateStrategy defines the update strategy to apply. + Examples: "semver", "latest", "digest", "name". + This acts as the default if not overridden at a more specific level. + type: string + type: object + imageName: + description: |- + ImageName is the full identifier of the image to be tracked, + including the registry (if not Docker Hub), the image name, and an initial/current tag or version. + This is the string used to query the container registry and also as a base for finding updates. + Example: "docker.io/library/nginx:1.17.10", "quay.io/prometheus/node-exporter:v1.5.0". + This field is mandatory. + type: string + manifestTargets: + description: |- + ManifestTarget defines how and where to update this image in Kubernetes manifests. + Only one of Helm or Kustomize should be specified within this block. + This whole block is optional if the image update isn't written to a manifest in a structured way. + properties: + helm: + description: |- + Helm specifies update parameters if the target manifest is managed by Helm + and updates are to be made to Helm values files. + properties: + name: + description: |- + Name is the dot-separated path to the Helm key for the image repository/name part. + Example: "image.repository", "frontend.deployment.image.name". + If neither spec nor name/tag are set, defaults to "image.name". + If spec is set, this field is ignored. + type: string + spec: + description: |- + Spec is the dot-separated path to a Helm key where the full image string + (e.g., "image/name:1.0") should be written. + Use this if your Helm chart expects the entire image reference in a single field, + rather than separate name/tag fields. If this is set, name and tag will be ignored. + type: string + tag: + description: |- + Tag is the dot-separated path to the Helm key for the image tag part. + Example: "image.tag", "frontend.deployment.image.version". + If neither spec nor name/tag are set, defaults to "image.tag". + If spec is set, this field is ignored. + type: string + type: object + kustomize: + description: |- + Kustomize specifies update parameters if the target manifest is managed by Kustomize + and updates involve changing image tags in Kustomize configurations. + properties: + name: + description: |- + Name is the image name (which can include the registry and an initial tag) + as it appears in the `images` list of a kustomization.yaml file that needs to be updated. + The updater will typically change the tag or add a digest to this entry. + Example: "docker.io/library/nginx". + This field is required if the Kustomize target is used. + type: string + required: + - name + type: object + type: object + x-kubernetes-validations: + - message: Exactly one of helm or kustomize must be specified + within manifestTargets if the block is present. + rule: 'has(self.helm) ? !has(self.kustomize) : has(self.kustomize)' + required: + - alias + - imageName + type: object + type: array + x-kubernetes-list-map-keys: + - alias + x-kubernetes-list-type: map + labelSelectors: + description: LabelSelectors indicates the label selectors to + apply for application selection + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namePattern: + description: NamePattern indicates the glob pattern for application + name + type: string + useAnnotations: + default: false + description: |- + UseAnnotations When true, read image configuration from Application's + argocd-image-updater.argoproj.io/* annotations instead of + requiring explicit Images[] configuration in this CR. + When this field is set to true, only namePattern and labelSelectors are used for + application selection. All other fields (CommonUpdateSettings, WriteBackConfig, Images) + are ignored. + type: boolean + writeBackConfig: + description: |- + WriteBackConfig overrides the global WriteBackConfig settings for applications + matched by this selector. + This field is ignored when UseAnnotations is true. + properties: + gitConfig: + description: |- + GitConfig provides Git configuration settings if the write-back method involves Git. + This can only be used when method is "git" or starts with "git:". + properties: + branch: + description: |- + Branch to commit updates to. + Required if write-back method is Git and this is not specified at the spec level. + type: string + repository: + description: |- + Repository URL to commit changes to. + If not specified here or at the spec level, the controller MUST infer it from the + Argo CD Application's `spec.source.repoURL`. This field allows overriding that. + type: string + writeBackTarget: + description: |- + WriteBackTarget defines the path and type of file to update in the Git repository. + Examples: "helmvalues:./helm/values.yaml", "kustomization:./kustomize/overlays/production". + For ApplicationSet usage, `{{ .app.path.path }}` should be resolved by ApplicationSet + before this CR is generated, resulting in a concrete path here. + Required if write-back method is Git and this is not specified at the spec level. + type: string + type: object + method: + default: argocd + description: |- + Method defines the method for writing back updated image versions. + This acts as the default if not overridden. If not specified, defaults to "argocd". + pattern: ^(argocd|git|git:[a-zA-Z0-9][a-zA-Z0-9-._/:]*)$ + type: string + required: + - method + type: object + required: + - namePattern + type: object + x-kubernetes-validations: + - message: Either useAnnotations must be true, or images must be + provided with at least one item + rule: '!(has(self.useAnnotations) && self.useAnnotations == true) + ? (has(self.images) && size(self.images) > 0) : true' + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - namePattern + x-kubernetes-list-type: map + commonUpdateSettings: + description: |- + CommonUpdateSettings provides global default settings for update strategies, + tag filtering, pull secrets, etc., for all applications matched by this CR. + These can be overridden at the ApplicationRef or ImageConfig level. + properties: + allowTags: + description: |- + AllowTags is a regex pattern for tags to allow. + This acts as the default if not overridden. + type: string + forceUpdate: + default: false + description: |- + ForceUpdate specifies whether updates should be forced. + This acts as the default if not overridden. + type: boolean + ignoreTags: + description: |- + IgnoreTags is a list of glob-like patterns of tags to ignore. + This acts as the default and can be overridden at more specific levels. + items: + type: string + type: array + x-kubernetes-list-type: atomic + platforms: + description: |- + Platforms specifies a list of target platforms (e.g., "linux/amd64", "linux/arm64"). + If specified, the image updater will consider these platforms when checking for new versions or digests. + items: + type: string + type: array + x-kubernetes-list-type: atomic + pullSecret: + description: |- + PullSecret is the pull secret to use for images. + This acts as the default if not overridden. + type: string + updateStrategy: + default: semver + description: |- + UpdateStrategy defines the update strategy to apply. + Examples: "semver", "latest", "digest", "name". + This acts as the default if not overridden at a more specific level. + type: string + type: object + namespace: + description: |- + Namespace indicates the target namespace of the applications. + + Deprecated: This field is deprecated and will be removed in a future release. + The controller now uses the ImageUpdater CR's namespace (metadata.namespace) + to determine which namespace to search for applications. This field is ignored. + type: string + writeBackConfig: + description: |- + WriteBackConfig provides global default settings for how and where to write back image updates. + This can be overridden at the ApplicationRef level. + properties: + gitConfig: + description: |- + GitConfig provides Git configuration settings if the write-back method involves Git. + This can only be used when method is "git" or starts with "git:". + properties: + branch: + description: |- + Branch to commit updates to. + Required if write-back method is Git and this is not specified at the spec level. + type: string + repository: + description: |- + Repository URL to commit changes to. + If not specified here or at the spec level, the controller MUST infer it from the + Argo CD Application's `spec.source.repoURL`. This field allows overriding that. + type: string + writeBackTarget: + description: |- + WriteBackTarget defines the path and type of file to update in the Git repository. + Examples: "helmvalues:./helm/values.yaml", "kustomization:./kustomize/overlays/production". + For ApplicationSet usage, `{{ .app.path.path }}` should be resolved by ApplicationSet + before this CR is generated, resulting in a concrete path here. + Required if write-back method is Git and this is not specified at the spec level. + type: string + type: object + method: + default: argocd + description: |- + Method defines the method for writing back updated image versions. + This acts as the default if not overridden. If not specified, defaults to "argocd". + pattern: ^(argocd|git|git:[a-zA-Z0-9][a-zA-Z0-9-._/:]*)$ + type: string + required: + - method + type: object + required: + - applicationRefs + type: object + status: + description: ImageUpdaterStatus defines the observed state of ImageUpdater + properties: + conditions: + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + imageStatus: + description: ImageStatus indicates the detailed status for the list + of managed images + items: + description: ImageStatus contains information for an image:version + and its update status in hosting applications + properties: + applications: + description: Applications contains a list of applications and + when the image was last updated therein + items: + description: ImageApplicationLastUpdated contains information + for an application and when the image was last updated therein + properties: + appName: + description: AppName indicates and namespace and the application + name + type: string + lastUpdatedAt: + description: LastUpdatedAt indicates when the image in + this application was last updated + format: date-time + type: string + required: + - appName + type: object + type: array + name: + description: Name indicates the image name + type: string + version: + description: Version indicates the image version + type: string + required: + - name + - version + type: object + type: array + reconciledAt: + description: LastUpdatedAt indicates when the image updater last ran + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 77cc696c3..b9d5e0a27 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -9,6 +9,7 @@ resources: - bases/argoproj.io_argocds.yaml - bases/argoproj.io_namespacemanagements.yaml - bases/argoproj.io_notificationsconfigurations.yaml +- bases/argocd-image-updater.argoproj.io_imageupdaters.yaml - bases/analysis-run-crd.yaml - bases/analysis-template-crd.yaml - bases/argoproj.io_rolloutmanagers.yaml diff --git a/config/manifests/bases/gitops-operator.clusterserviceversion.yaml b/config/manifests/bases/gitops-operator.clusterserviceversion.yaml index 2aeac140a..c91e1f76b 100644 --- a/config/manifests/bases/gitops-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/gitops-operator.clusterserviceversion.yaml @@ -132,6 +132,11 @@ spec: kind: RolloutManager name: rolloutmanagers.argoproj.io version: v1alpha1 + - description: ImageUpdater is the Schema for the imageupdaters API + displayName: ImageUpdater + kind: ImageUpdater + name: imageupdaters.argocd-image-updater.argoproj.io + version: v1alpha1 - description: GitopsService is the Schema for the gitopsservices API displayName: Gitops Service kind: GitopsService diff --git a/go.mod b/go.mod index 13e6abcda..3dbdc6f47 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.25.5 require ( github.com/argoproj-labs/argo-rollouts-manager v0.0.8-0.20260218104514-432c01ce417a + github.com/argoproj-labs/argocd-image-updater v1.1.0 github.com/argoproj-labs/argocd-operator v0.17.0-rc1.0.20260211145236-4c05ef8fa3d7 github.com/argoproj/argo-cd/v3 v3.3.0 github.com/argoproj/gitops-engine v0.7.1-0.20251217140045-5baed5604d2d @@ -58,6 +59,7 @@ require ( github.com/chai2010/gettext-go v1.0.3 // indirect github.com/chainguard-dev/git-urls v1.0.2 // indirect github.com/cloudflare/circl v1.6.1 // indirect + github.com/coreos/go-oidc/v3 v3.14.1 // indirect github.com/cyphar/filepath-securejoin v0.6.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect @@ -68,12 +70,15 @@ require ( github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/fatih/camelcase v1.0.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-errors/errors v1.5.1 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect github.com/go-git/go-git/v5 v5.16.5 // indirect + github.com/go-jose/go-jose/v4 v4.1.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.22.1 // indirect github.com/go-openapi/jsonreference v0.21.3 // indirect @@ -86,6 +91,7 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-github/v69 v69.2.0 // indirect @@ -94,6 +100,10 @@ require ( github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 // indirect github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-retryablehttp v0.7.8 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jonboulle/clockwork v0.5.0 // indirect @@ -125,6 +135,7 @@ require ( github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.66.1 // indirect github.com/prometheus/procfs v0.16.1 // indirect + github.com/r3labs/diff/v3 v3.0.2 // indirect github.com/redis/go-redis/v9 v9.8.0 // indirect github.com/robfig/cron/v3 v3.0.2-0.20210106135023-bc59245fe10e // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect @@ -140,7 +151,10 @@ require ( github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xlab/treeprint v1.2.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel/metric v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect @@ -155,6 +169,8 @@ require ( golang.org/x/time v0.14.0 // indirect golang.org/x/tools v0.41.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect google.golang.org/grpc v1.77.0 // indirect google.golang.org/protobuf v1.36.11 // indirect diff --git a/go.sum b/go.sum index 84f71483a..2c068443c 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cyphar.com/go-pathrs v0.2.1 h1:9nx1vOgwVvX1mNBWDu93+vaceedpbsDqo+XuBGL40b8= @@ -18,6 +20,7 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs= github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= @@ -31,8 +34,11 @@ github.com/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21j github.com/alicebob/miniredis/v2 v2.35.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/argoproj-labs/argo-rollouts-manager v0.0.8-0.20260218104514-432c01ce417a h1:USjEzxbs2lZtx7+Hp9u5dYgu7pf/9XnDUSc9+Hmulmo= github.com/argoproj-labs/argo-rollouts-manager v0.0.8-0.20260218104514-432c01ce417a/go.mod h1:WPyZkNHZjir/OTt8mrRwcUZKe1euHrHPJsRv1Wp/F/0= +github.com/argoproj-labs/argocd-image-updater v1.1.0 h1:XR+xZf8bDFBaTpVdVpe06t/DPmrIG4BG3HukUXul6X0= +github.com/argoproj-labs/argocd-image-updater v1.1.0/go.mod h1:RbPRnEqWBPq1OP29vlZjmfL+/NfonpoagH8SInP/YHc= github.com/argoproj-labs/argocd-operator v0.17.0-rc1.0.20260211145236-4c05ef8fa3d7 h1:SF89hDvomBhku9IjRO60fzeS8ZdwHgmo7KhfTLF4tYo= github.com/argoproj-labs/argocd-operator v0.17.0-rc1.0.20260211145236-4c05ef8fa3d7/go.mod h1:G9rmG9/3gV899eg8wL/4YQYTBSq5M+xEwfVBMuE8RlA= github.com/argoproj/argo-cd/v3 v3.3.0 h1:9UlruTd5cC/MyvorTXgAIblfZTy63MF5FYvvoAaUvwU= @@ -67,6 +73,7 @@ github.com/casbin/govaluate v1.10.0 h1:ffGw51/hYH3w3rZcxO/KcaUIDOLP84w7nsidMVgaD github.com/casbin/govaluate v1.10.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A= github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cert-manager/cert-manager v1.15.4 h1:FtH6BOTmkNBNRjoYSW2b80MYpUq4Zw1zbEB6flYzkiM= github.com/cert-manager/cert-manager v1.15.4/go.mod h1:stBge/DTvrhfQMB/93+Y62s+gQgZBsfL1o0C/4AL/mI= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -80,8 +87,12 @@ github.com/chainguard-dev/git-urls v1.0.2/go.mod h1:rbGgj10OS7UgZlbzdUQIQpT0k/D4 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk= +github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= @@ -104,6 +115,10 @@ github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8= github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= @@ -112,12 +127,17 @@ github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2 github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= @@ -138,6 +158,7 @@ github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M= github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -174,8 +195,10 @@ github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXe github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= @@ -184,6 +207,8 @@ github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -202,6 +227,7 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 h1:z2ogiKUYzX5Is6zr/vP9vJGqPwcdqsWjOt+V8J7+bTc= github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.1-0.20241114170450-2d3c2a9cc518 h1:UBg1xk+oAsIVbFuGg6hdfAm7EvCv3EL80vFxJNsslqw= github.com/google/uuid v1.6.1-0.20241114170450-2d3c2a9cc518/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -213,6 +239,12 @@ github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajR github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= +github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -260,6 +292,10 @@ github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4 github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= @@ -336,6 +372,7 @@ github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.73.2 h github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.73.2/go.mod h1:yJ3CawR/A5qEYFEeCOUVYLTwYxmacfHQhJS+b/2QiaM= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= @@ -349,6 +386,7 @@ github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhi github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/robfig/cron/v3 v3.0.2-0.20210106135023-bc59245fe10e h1:0xChnl3lhHiXbgSJKgChye0D+DvoItkOdkGcwelDXH0= github.com/robfig/cron/v3 v3.0.2-0.20210106135023-bc59245fe10e/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= @@ -419,6 +457,10 @@ go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -441,6 +483,10 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= @@ -449,12 +495,17 @@ golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= @@ -469,9 +520,13 @@ golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -480,6 +535,7 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -529,7 +585,11 @@ golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -546,12 +606,24 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda h1:wu/KJm9KJwpfHWhkkZGohVC6KRrc1oJNr4jwtQMOQXw= google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda/go.mod h1:g2LLCvCeCSir/JJSWosk19BR4NVxGqHUC6rxIRsd7Aw= google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4= google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo= google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 h1:M1rk8KBnUsBDg1oPGHNCxG4vc1f49epmTO7xscSajMk= google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= @@ -580,6 +652,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= diff --git a/test/openshift/e2e/ginkgo/fixture/utils/fixtureUtils.go b/test/openshift/e2e/ginkgo/fixture/utils/fixtureUtils.go index 84f17ca76..97f3bcc76 100644 --- a/test/openshift/e2e/ginkgo/fixture/utils/fixtureUtils.go +++ b/test/openshift/e2e/ginkgo/fixture/utils/fixtureUtils.go @@ -16,6 +16,7 @@ import ( olmv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" rolloutmanagerv1alpha1 "github.com/argoproj-labs/argo-rollouts-manager/api/v1alpha1" + imageUpdater "github.com/argoproj-labs/argocd-image-updater/api/v1alpha1" argov1alpha1api "github.com/argoproj-labs/argocd-operator/api/v1alpha1" consolev1 "github.com/openshift/api/console/v1" routev1 "github.com/openshift/api/route/v1" @@ -141,6 +142,10 @@ func getKubeClient(config *rest.Config) (client.Client, *runtime.Scheme, error) return nil, nil, err } + if err := imageUpdater.AddToScheme(scheme); err != nil { + return nil, nil, err + } + k8sClient, err := client.New(config, client.Options{Scheme: scheme}) if err != nil { return nil, nil, err diff --git a/test/openshift/e2e/ginkgo/parallel/1-034_validate_webhook_notifications_test.go b/test/openshift/e2e/ginkgo/parallel/1-034_validate_webhook_notifications_test.go index 1772b66d9..60f5b814c 100644 --- a/test/openshift/e2e/ginkgo/parallel/1-034_validate_webhook_notifications_test.go +++ b/test/openshift/e2e/ginkgo/parallel/1-034_validate_webhook_notifications_test.go @@ -419,7 +419,7 @@ UVwpFuaKz5vTCD36Gmmy/u8y return strings.Contains(out, `{"created":"my-app-3","type":"Directory"}`) - }, "4m", "5s").Should(BeTrue()) + }, "5m", "10s").Should(BeTrue(), "Webhook did not receive the expected notification within timeout") }) diff --git a/test/openshift/e2e/ginkgo/parallel/1-046_validate_application_tracking_test.go b/test/openshift/e2e/ginkgo/parallel/1-046_validate_application_tracking_test.go new file mode 100644 index 000000000..4ac8e54a8 --- /dev/null +++ b/test/openshift/e2e/ginkgo/parallel/1-046_validate_application_tracking_test.go @@ -0,0 +1,320 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package parallel + +import ( + "context" + + argocdv1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" + "github.com/argoproj/gitops-engine/pkg/health" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + argov1beta1api "github.com/argoproj-labs/argocd-operator/api/v1beta1" + "github.com/argoproj-labs/argocd-operator/tests/ginkgo/fixture" + "github.com/argoproj-labs/argocd-operator/tests/ginkgo/fixture/application" + argocdFixture "github.com/argoproj-labs/argocd-operator/tests/ginkgo/fixture/argocd" + configmapFixture "github.com/argoproj-labs/argocd-operator/tests/ginkgo/fixture/configmap" + k8sFixture "github.com/argoproj-labs/argocd-operator/tests/ginkgo/fixture/k8s" + "github.com/argoproj-labs/argocd-operator/tests/ginkgo/fixture/namespace" + fixtureUtils "github.com/argoproj-labs/argocd-operator/tests/ginkgo/fixture/utils" +) + +var _ = Describe("GitOps Operator Parallel E2E Tests", func() { + + Context("1-046_validate_application_tracking", func() { + + var ( + k8sClient client.Client + ctx context.Context + ) + + BeforeEach(func() { + fixture.EnsureParallelCleanSlate() + + k8sClient, _ = fixtureUtils.GetE2ETestKubeClient() + ctx = context.Background() + + }) + + It("verifies that when .spec.installationID is set, that value is set on Argo CD ConfigMap, and that installationID is also set on resources deployed by that Argo CD instance, and that .spec.resourceTrackingMethod is defined on that Argo CD instance", func() { + + By("creating namespaces which will contain Argo CD instances and which will be deployed to by Argo CD ") + test_1_046_argocd_1_NS, cleanupFunc := fixture.CreateNamespaceWithCleanupFunc("test-1-046-argocd-1") + defer cleanupFunc() + + test_1_046_argocd_2_NS, cleanupFunc := fixture.CreateNamespaceWithCleanupFunc("test-1-046-argocd-2") + defer cleanupFunc() + + test_1_046_argocd_3_NS, cleanupFunc := fixture.CreateNamespaceWithCleanupFunc("test-1-046-argocd-3") + defer cleanupFunc() + + source_ns_1_NS, cleanupFunc := fixture.CreateNamespaceWithCleanupFunc("source-ns-1") + defer cleanupFunc() + + source_ns_2_NS, cleanupFunc := fixture.CreateNamespaceWithCleanupFunc("source-ns-2") + defer cleanupFunc() + + source_ns_3_NS, cleanupFunc := fixture.CreateNamespaceWithCleanupFunc("source-ns-3") + defer cleanupFunc() + + By("creating first Argo CD instance, with installationID 'instance-1', and annotation+label tracking") + argocd_1 := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-1", + Namespace: test_1_046_argocd_1_NS.Name, + }, + Spec: argov1beta1api.ArgoCDSpec{ + InstallationID: "instance-1", + ResourceTrackingMethod: "annotation+label", + }, + } + Expect(k8sClient.Create(ctx, argocd_1)).Should(Succeed()) + + By("creating second Argo CD instance, with instance-2 ID, and annotation+label tracking") + argocd_2 := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-2", + Namespace: test_1_046_argocd_2_NS.Name, + }, + Spec: argov1beta1api.ArgoCDSpec{ + InstallationID: "instance-2", + ResourceTrackingMethod: "annotation+label", + }, + } + Expect(k8sClient.Create(ctx, argocd_2)).Should(Succeed()) + By("creating second Argo CD instance, with instance-3 ID, and annotation tracking (by default it is annotation") + argocd_3 := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-3", + Namespace: test_1_046_argocd_3_NS.Name, + }, + Spec: argov1beta1api.ArgoCDSpec{ + InstallationID: "instance-3", + }, + } + Expect(k8sClient.Create(ctx, argocd_3)).Should(Succeed()) + + Eventually(argocd_1, "5m", "5s").Should(argocdFixture.BeAvailable()) + Eventually(argocd_2, "5m", "5s").Should(argocdFixture.BeAvailable()) + Eventually(argocd_3, "5m", "5s").Should(argocdFixture.BeAvailable()) + + By("verifying argocd-cm for Argo CD instances contain the values defined in ArgoCD CR .spec field") + configMap_test_1_046_argocd_1 := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-cm", + Namespace: "test-1-046-argocd-1", + }, + } + Eventually(configMap_test_1_046_argocd_1).Should(k8sFixture.ExistByName()) + Expect(configMap_test_1_046_argocd_1).Should(configmapFixture.HaveStringDataKeyValue("installationID", "instance-1")) + Expect(configMap_test_1_046_argocd_1).Should(configmapFixture.HaveStringDataKeyValue("application.resourceTrackingMethod", "annotation+label")) + + configMap_test_1_046_argocd_2 := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-cm", + Namespace: "test-1-046-argocd-2", + }, + } + + Eventually(configMap_test_1_046_argocd_2).Should(k8sFixture.ExistByName()) + Expect(configMap_test_1_046_argocd_2).Should(configmapFixture.HaveStringDataKeyValue("installationID", "instance-2")) + Expect(configMap_test_1_046_argocd_2).Should(configmapFixture.HaveStringDataKeyValue("application.resourceTrackingMethod", "annotation+label")) + + configMap_test_1_046_argocd_3 := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-cm", + Namespace: "test-1-046-argocd-3", + }, + } + + Eventually(configMap_test_1_046_argocd_2).Should(k8sFixture.ExistByName()) + Expect(configMap_test_1_046_argocd_3).Should(configmapFixture.HaveStringDataKeyValue("installationID", "instance-3")) + Expect(configMap_test_1_046_argocd_3).Should(configmapFixture.HaveStringDataKeyValue("application.resourceTrackingMethod", "annotation")) + + By("adding managed-by label to test-1-046-argocd-(1/3), managed by Argo CD instances 1, 2 and 3") + namespace.Update(source_ns_1_NS, func(n *corev1.Namespace) { + if n.Labels == nil { + n.Labels = map[string]string{} + } + n.Labels["argocd.argoproj.io/managed-by"] = "test-1-046-argocd-1" + }) + + namespace.Update(source_ns_2_NS, func(n *corev1.Namespace) { + if n.Labels == nil { + n.Labels = map[string]string{} + } + n.Labels["argocd.argoproj.io/managed-by"] = "test-1-046-argocd-2" + }) + + namespace.Update(source_ns_3_NS, func(n *corev1.Namespace) { + n.Labels["argocd.argoproj.io/managed-by"] = "test-1-046-argocd-3" + if n.Annotations == nil { + n.Annotations = map[string]string{} + } + n.Annotations["argocd.argoproj.io/managed-by"] = "test-1-046-argocd-3" + }) + + By("verifying role is created in the correct source-ns-(1/3) namespaces, for instances") + role_appController_source_ns_1 := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-1-argocd-application-controller", + Namespace: "source-ns-1", + }, + } + Eventually(role_appController_source_ns_1).Should(k8sFixture.ExistByName()) + + role_appController_source_ns_2 := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-2-argocd-application-controller", + Namespace: "source-ns-2", + }, + } + Eventually(role_appController_source_ns_2).Should(k8sFixture.ExistByName()) + + role_appController_source_ns_3 := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-3-argocd-application-controller", + Namespace: "source-ns-3", + }, + } + Eventually(role_appController_source_ns_3).Should(k8sFixture.ExistByName()) + + By("by defining a simple Argo CD Application for both Argo CD instances, to deploy to source namespaces 1/2 respectively") + application_test_1_046_argocd_1 := &argocdv1alpha1.Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-app", + Namespace: "test-1-046-argocd-1", + }, + Spec: argocdv1alpha1.ApplicationSpec{ + Project: "default", + Source: &argocdv1alpha1.ApplicationSource{ + RepoURL: "https://github.com/redhat-developer/gitops-operator", + Path: "test/examples/nginx", + TargetRevision: "HEAD", + }, + Destination: argocdv1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "source-ns-1", + }, + SyncPolicy: &argocdv1alpha1.SyncPolicy{ + Automated: &argocdv1alpha1.SyncPolicyAutomated{}, + }, + }, + } + Expect(k8sClient.Create(ctx, application_test_1_046_argocd_1)).To(Succeed()) + + application_test_1_046_argocd_2 := &argocdv1alpha1.Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-app", + Namespace: "test-1-046-argocd-2", + }, + Spec: argocdv1alpha1.ApplicationSpec{ + Project: "default", + Source: &argocdv1alpha1.ApplicationSource{ + RepoURL: "https://github.com/redhat-developer/gitops-operator", + Path: "test/examples/nginx", + TargetRevision: "HEAD", + }, + Destination: argocdv1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "source-ns-2", + }, + SyncPolicy: &argocdv1alpha1.SyncPolicy{ + Automated: &argocdv1alpha1.SyncPolicyAutomated{}, + }, + }, + } + Expect(k8sClient.Create(ctx, application_test_1_046_argocd_2)).To(Succeed()) + application_test_1_046_argocd_3 := &argocdv1alpha1.Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-app", + Namespace: "test-1-046-argocd-3", + }, + Spec: argocdv1alpha1.ApplicationSpec{ + Project: "default", + Source: &argocdv1alpha1.ApplicationSource{ + RepoURL: "https://github.com/redhat-developer/gitops-operator", + Path: "test/examples/nginx", + TargetRevision: "HEAD", + }, + Destination: argocdv1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "source-ns-3", + }, + SyncPolicy: &argocdv1alpha1.SyncPolicy{ + Automated: &argocdv1alpha1.SyncPolicyAutomated{}, + }, + }, + } + Expect(k8sClient.Create(ctx, application_test_1_046_argocd_3)).To(Succeed()) + + By("verifying that the Applications successfully deployed, and that they have the correct installation-id and tracking-id, based on which Argo CD instance deployed them") + + Eventually(application_test_1_046_argocd_1, "4m", "5s").Should(application.HaveHealthStatusCode(health.HealthStatusHealthy)) + Eventually(application_test_1_046_argocd_1, "4m", "5s").Should(application.HaveSyncStatusCode(argocdv1alpha1.SyncStatusCodeSynced)) + + Eventually(application_test_1_046_argocd_2, "4m", "5s").Should(application.HaveHealthStatusCode(health.HealthStatusHealthy)) + Eventually(application_test_1_046_argocd_2, "4m", "5s").Should(application.HaveSyncStatusCode(argocdv1alpha1.SyncStatusCodeSynced)) + + Eventually(application_test_1_046_argocd_3, "4m", "5s").Should(application.HaveHealthStatusCode(health.HealthStatusHealthy)) + Eventually(application_test_1_046_argocd_3, "4m", "5s").Should(application.HaveSyncStatusCode(argocdv1alpha1.SyncStatusCodeSynced)) + + deployment_source_ns_1 := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx-deployment", + Namespace: "source-ns-1", + }, + } + Eventually(deployment_source_ns_1).Should(k8sFixture.ExistByName()) + Eventually(deployment_source_ns_1).Should(k8sFixture.HaveAnnotationWithValue("argocd.argoproj.io/installation-id", "instance-1")) + Eventually(deployment_source_ns_1).Should(k8sFixture.HaveAnnotationWithValue("argocd.argoproj.io/tracking-id", "test-app:apps/Deployment:source-ns-1/nginx-deployment")) + + Eventually(deployment_source_ns_1).Should(k8sFixture.HaveLabelWithValue("app.kubernetes.io/instance", "test-app")) + + deployment_source_ns_2 := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx-deployment", + Namespace: "source-ns-2", + }, + } + Eventually(deployment_source_ns_2).Should(k8sFixture.ExistByName()) + Eventually(deployment_source_ns_2).Should(k8sFixture.HaveAnnotationWithValue("argocd.argoproj.io/installation-id", "instance-2")) + Eventually(deployment_source_ns_2).Should(k8sFixture.HaveAnnotationWithValue("argocd.argoproj.io/tracking-id", "test-app:apps/Deployment:source-ns-2/nginx-deployment")) + + Eventually(deployment_source_ns_2).Should(k8sFixture.HaveLabelWithValue("app.kubernetes.io/instance", "test-app")) + + deployment_source_ns_3 := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx-deployment", + Namespace: "source-ns-3", + }, + } + Eventually(deployment_source_ns_3).Should(k8sFixture.ExistByName()) + Eventually(deployment_source_ns_3).Should(k8sFixture.HaveAnnotationWithValue("argocd.argoproj.io/installation-id", "instance-3")) + Eventually(deployment_source_ns_3).Should(k8sFixture.HaveAnnotationWithValue("argocd.argoproj.io/tracking-id", "test-app:apps/Deployment:source-ns-3/nginx-deployment")) + + Eventually(deployment_source_ns_3).Should(k8sFixture.NotHaveLabelWithValue("app.kubernetes.io/instance", "test-app")) + }) + + }) +}) diff --git a/test/openshift/e2e/ginkgo/parallel/1-054_validate_deploymentconfig_test.go b/test/openshift/e2e/ginkgo/parallel/1-054_validate_deploymentconfig_test.go index 28c120059..d88c35ab5 100644 --- a/test/openshift/e2e/ginkgo/parallel/1-054_validate_deploymentconfig_test.go +++ b/test/openshift/e2e/ginkgo/parallel/1-054_validate_deploymentconfig_test.go @@ -93,8 +93,8 @@ var _ = Describe("GitOps Operator Parallel E2E Tests", func() { Expect(k8sClient.Create(ctx, app)).To(Succeed()) By("verifying Application is healthy and sync operation succeeded") - Eventually(app).Should(applicationFixture.HaveHealthStatusCode(health.HealthStatusHealthy)) - Eventually(app).Should(applicationFixture.HaveOperationStatePhase(common.OperationSucceeded)) + Eventually(app, "8m", "10s").Should(applicationFixture.HaveHealthStatusCode(health.HealthStatusHealthy), "Application did not reach healthy status within timeout") + Eventually(app, "8m", "10s").Should(applicationFixture.HaveOperationStatePhase(common.OperationSucceeded), "Application operation did not succeed within timeout") By("verifying DeploymentConfig has 2 replicas") dc := &osappsv1.DeploymentConfig{ @@ -124,8 +124,8 @@ var _ = Describe("GitOps Operator Parallel E2E Tests", func() { }, "2m", "1s").Should(BeTrue()) By("verifying Application is still healthy and operation has succeeded") - Eventually(app).Should(applicationFixture.HaveHealthStatusCode(health.HealthStatusHealthy)) - Eventually(app).Should(applicationFixture.HaveOperationStatePhase(common.OperationSucceeded)) + Eventually(app, "8m", "10s").Should(applicationFixture.HaveHealthStatusCode(health.HealthStatusHealthy), "Application did not reach healthy status after update within timeout") + Eventually(app, "8m", "10s").Should(applicationFixture.HaveOperationStatePhase(common.OperationSucceeded), "Application operation did not succeed after update within timeout") }) diff --git a/test/openshift/e2e/ginkgo/parallel/1-058_validate_prometheus_rule_test.go b/test/openshift/e2e/ginkgo/parallel/1-058_validate_prometheus_rule_test.go index 62fd4b5a8..d3a717f3d 100644 --- a/test/openshift/e2e/ginkgo/parallel/1-058_validate_prometheus_rule_test.go +++ b/test/openshift/e2e/ginkgo/parallel/1-058_validate_prometheus_rule_test.go @@ -91,8 +91,8 @@ var _ = Describe("GitOps Operator Parallel E2E Tests", func() { } Expect(k8sClient.Create(ctx, app)).To(Succeed()) - Eventually(app).Should(applicationFixture.HaveHealthStatusCode(health.HealthStatusHealthy)) - Eventually(app).Should(applicationFixture.HaveSyncStatusCode(appv1alpha1.SyncStatusCodeSynced)) + Eventually(app, "8m", "10s").Should(applicationFixture.HaveHealthStatusCode(health.HealthStatusHealthy), "Application did not reach healthy status within timeout") + Eventually(app, "8m", "10s").Should(applicationFixture.HaveSyncStatusCode(appv1alpha1.SyncStatusCodeSynced), "Application did not sync within timeout") }) }) }) diff --git a/test/openshift/e2e/ginkgo/parallel/1-067_validate_redis_secure_comm_no_autotls_ha_test.go b/test/openshift/e2e/ginkgo/parallel/1-067_validate_redis_secure_comm_no_autotls_ha_test.go index a536fba9b..54d256960 100644 --- a/test/openshift/e2e/ginkgo/parallel/1-067_validate_redis_secure_comm_no_autotls_ha_test.go +++ b/test/openshift/e2e/ginkgo/parallel/1-067_validate_redis_secure_comm_no_autotls_ha_test.go @@ -87,15 +87,15 @@ var _ = Describe("GitOps Operator Parallel E2E Tests", func() { Eventually(argoCD, "5m", "10s").Should(argocdFixture.BeAvailable()) deploymentsShouldExist := []string{"argocd-redis-ha-haproxy", "argocd-server", "argocd-repo-server"} - for _, depl := range deploymentsShouldExist { + for _, deplName := range deploymentsShouldExist { replicas := 1 - if depl == "argocd-redis-ha-haproxy" { + if deplName == "argocd-redis-ha-haproxy" { replicas = 3 } - depl := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: depl, Namespace: ns.Name}} - Eventually(depl).Should(k8sFixture.ExistByName()) - Eventually(depl).Should(deplFixture.HaveReadyReplicas(replicas)) + depl := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: deplName, Namespace: ns.Name}} + Eventually(depl, "2m", "5s").Should(k8sFixture.ExistByName(), "Deployment "+deplName+" did not exist within timeout") + Eventually(depl, "6m", "10s").Should(deplFixture.HaveReadyReplicas(replicas), "Deployment "+deplName+" did not have ready replicas within timeout") } statefulsetsShouldExist := []string{"argocd-redis-ha-server", "argocd-application-controller"} @@ -107,9 +107,9 @@ var _ = Describe("GitOps Operator Parallel E2E Tests", func() { } statefulSet := &appsv1.StatefulSet{ObjectMeta: metav1.ObjectMeta{Name: ss, Namespace: ns.Name}} - Eventually(statefulSet).Should(k8sFixture.ExistByName()) - Eventually(statefulSet).Should(statefulsetFixture.HaveReplicas(replicas)) - Eventually(statefulSet).Should(statefulsetFixture.HaveReadyReplicas(replicas)) + Eventually(statefulSet, "2m", "5s").Should(k8sFixture.ExistByName(), "StatefulSet "+ss+" did not exist within timeout") + Eventually(statefulSet, "3m", "5s").Should(statefulsetFixture.HaveReplicas(replicas), "StatefulSet "+ss+" did not have correct replicas within timeout") + Eventually(statefulSet, "6m", "10s").Should(statefulsetFixture.HaveReadyReplicas(replicas), "StatefulSet "+ss+" did not have ready replicas within timeout") } } @@ -191,14 +191,14 @@ var _ = Describe("GitOps Operator Parallel E2E Tests", func() { } repoServerDepl := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "argocd-repo-server", Namespace: ns.Name}} - Eventually(repoServerDepl).Should(k8sFixture.ExistByName()) + Eventually(repoServerDepl, "2m", "5s").Should(k8sFixture.ExistByName(), "Repo server deployment did not exist within timeout") By("expecting repo-server to have desired container process command/arguments") Expect(repoServerDepl).To(deplFixture.HaveContainerCommandSubstring("uid_entrypoint.sh argocd-repo-server --redis argocd-redis-ha-haproxy."+ns.Name+".svc.cluster.local:6379 --redis-use-tls --redis-ca-certificate /app/config/reposerver/tls/redis/tls.crt --loglevel info --logformat text", 0), "TLS .spec.template.spec.containers.command for argocd-repo-server deployment is wrong") argocdServerDepl := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "argocd-server", Namespace: ns.Name}} - Eventually(argocdServerDepl).Should(k8sFixture.ExistByName()) + Eventually(argocdServerDepl, "2m", "5s").Should(k8sFixture.ExistByName(), "ArgoCD server deployment did not exist within timeout") By("expecting argocd-server to have desired container process command/arguments") Expect(argocdServerDepl).To(deplFixture.HaveContainerCommandSubstring("argocd-server --staticassets /shared/app --dex-server https://argocd-dex-server."+ns.Name+".svc.cluster.local:5556 --repo-server argocd-repo-server."+ns.Name+".svc.cluster.local:8081 --redis argocd-redis-ha-haproxy."+ns.Name+".svc.cluster.local:6379 --redis-use-tls --redis-ca-certificate /app/config/server/tls/redis/tls.crt --loglevel info --logformat text", 0), @@ -206,7 +206,7 @@ var _ = Describe("GitOps Operator Parallel E2E Tests", func() { By("expecting application-controller to have desired container process command/arguments") applicationControllerSS := &appsv1.StatefulSet{ObjectMeta: metav1.ObjectMeta{Name: "argocd-application-controller", Namespace: ns.Name}} - Eventually(applicationControllerSS).Should(k8sFixture.ExistByName()) + Eventually(applicationControllerSS, "2m", "5s").Should(k8sFixture.ExistByName(), "Application controller StatefulSet did not exist within timeout") Expect(applicationControllerSS).To(statefulsetFixture.HaveContainerCommandSubstring("argocd-application-controller --operation-processors 10 --redis argocd-redis-ha-haproxy."+ns.Name+".svc.cluster.local:6379 --redis-use-tls --redis-ca-certificate /app/config/controller/tls/redis/tls.crt --repo-server argocd-repo-server."+ns.Name+".svc.cluster.local:8081 --status-processors 20 --kubectl-parallelism-limit 10 --loglevel info --logformat text", 0), "TLS .spec.template.spec.containers.command for argocd-application-controller statefulsets is wrong") diff --git a/test/openshift/e2e/ginkgo/parallel/1-083_validate_kustomize_namereference_test.go b/test/openshift/e2e/ginkgo/parallel/1-083_validate_kustomize_namereference_test.go index 615edb6f3..a1b136d4a 100644 --- a/test/openshift/e2e/ginkgo/parallel/1-083_validate_kustomize_namereference_test.go +++ b/test/openshift/e2e/ginkgo/parallel/1-083_validate_kustomize_namereference_test.go @@ -104,9 +104,7 @@ var _ = Describe("GitOps Operator Parallel E2E Tests", func() { By("verifying that the ConfigMap is generated by Kustomize, within Argo CD, and deployed to the Namespace") configMap := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "gitops-configmap", Namespace: ns.Name}} - Eventually(configMap, "4m", "5s").Should(k8sFixture.ExistByName()) - - Expect(configMap.Annotations["foo"]).To(Equal("gitops-configmap")) + Eventually(configMap, "8m", "10s").Should(k8sFixture.ExistByName(), "ConfigMap did not exist within timeout") }) }) diff --git a/test/openshift/e2e/ginkgo/parallel/1-121_validate_image_updater_test.go b/test/openshift/e2e/ginkgo/parallel/1-121_validate_image_updater_test.go new file mode 100644 index 000000000..143489724 --- /dev/null +++ b/test/openshift/e2e/ginkgo/parallel/1-121_validate_image_updater_test.go @@ -0,0 +1,208 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package parallel + +import ( + "context" + "strings" + + appv1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" + "github.com/argoproj/gitops-engine/pkg/health" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + imageUpdaterApi "github.com/argoproj-labs/argocd-image-updater/api/v1alpha1" + + argov1beta1api "github.com/argoproj-labs/argocd-operator/api/v1beta1" + "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture" + applicationFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/application" + argocdFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/argocd" + deplFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/deployment" + k8sFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/k8s" + osFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/os" + ssFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/statefulset" + fixtureUtils "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/utils" +) + +var _ = Describe("GitOps Operator Parallel E2E Tests", func() { + + Context("1-121_validate_image_updater_test", func() { + + var ( + k8sClient client.Client + ctx context.Context + ns *corev1.Namespace + cleanupFunc func() + imageUpdater *imageUpdaterApi.ImageUpdater + ) + + BeforeEach(func() { + fixture.EnsureParallelCleanSlate() + + k8sClient, _ = fixtureUtils.GetE2ETestKubeClient() + ctx = context.Background() + }) + + AfterEach(func() { + if imageUpdater != nil { + By("deleting ImageUpdater CR") + Expect(k8sClient.Delete(ctx, imageUpdater)).To(Succeed()) + Eventually(imageUpdater).Should(k8sFixture.NotExistByName()) + } + + if cleanupFunc != nil { + cleanupFunc() + } + + fixture.OutputDebugOnFail(ns) + + }) + + It("ensures that Image Updater will update Argo CD Application to the latest image", func() { + + By("creating simple namespace-scoped Argo CD instance with image updater enabled") + ns, cleanupFunc = fixture.CreateRandomE2ETestNamespaceWithCleanupFunc() + + By("ensuring default service account has anyuid SCC permission") + serviceAccountUser := "system:serviceaccount:" + ns.Name + ":default" + output, err := osFixture.ExecCommand("oc", "auth", "can-i", "use", "scc/anyuid", "--as", serviceAccountUser) + hasPermission := false + if err == nil && len(output) > 0 { + // Check if the service account user is already in the users list + // Remove quotes and whitespace for comparison + output = strings.TrimSpace(strings.Trim(output, "'\"")) + if strings.Contains(output, serviceAccountUser) { + hasPermission = true + } + } + if !hasPermission { + _, err := osFixture.ExecCommand("oc", "adm", "policy", "add-scc-to-user", "anyuid", "-z", "default", "-n", ns.Name) + Expect(err).NotTo(HaveOccurred(), "Failed to add anyuid SCC to default service account") + } + + argoCD := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{Name: "argocd", Namespace: ns.Name}, + Spec: argov1beta1api.ArgoCDSpec{ + ImageUpdater: argov1beta1api.ArgoCDImageUpdaterSpec{ + Env: []corev1.EnvVar{ + { + Name: "IMAGE_UPDATER_LOGLEVEL", + Value: "trace", + }, + }, + Enabled: true}, + }, + } + Expect(k8sClient.Create(ctx, argoCD)).To(Succeed()) + + By("waiting for ArgoCD CR to be reconciled and the instance to be ready") + Eventually(argoCD, "5m", "5s").Should(argocdFixture.BeAvailable()) + + By("verifying all workloads are started") + deploymentsShouldExist := []string{"argocd-redis", "argocd-server", "argocd-repo-server", "argocd-argocd-image-updater-controller"} + for _, deplName := range deploymentsShouldExist { + depl := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: deplName, Namespace: ns.Name}} + Eventually(depl, "2m", "5s").Should(k8sFixture.ExistByName(), "Deployment "+deplName+" did not exist within timeout") + Eventually(depl, "2m", "5s").Should(deplFixture.HaveReplicas(1), "Deployment "+deplName+" did not have correct replicas within timeout") + Eventually(depl, "3m", "5s").Should(deplFixture.HaveReadyReplicas(1), "Deployment "+deplName+" was not ready within timeout") + } + + statefulSet := &appsv1.StatefulSet{ObjectMeta: metav1.ObjectMeta{Name: "argocd-application-controller", Namespace: ns.Name}} + Eventually(statefulSet).Should(k8sFixture.ExistByName()) + Eventually(statefulSet).Should(ssFixture.HaveReplicas(1)) + Eventually(statefulSet, "3m", "5s").Should(ssFixture.HaveReadyReplicas(1)) + + By("creating Application") + app := &appv1alpha1.Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app-01", + Namespace: ns.Name, + }, + Spec: appv1alpha1.ApplicationSpec{ + Project: "default", + Source: &appv1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj-labs/argocd-image-updater/", + Path: "test/e2e/testdata/005-public-guestbook", + TargetRevision: "HEAD", + }, + Destination: appv1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: ns.Name, + }, + SyncPolicy: &appv1alpha1.SyncPolicy{Automated: &appv1alpha1.SyncPolicyAutomated{}}, + }, + } + Expect(k8sClient.Create(ctx, app)).To(Succeed()) + + By("verifying deploying the Application succeeded") + Eventually(app, "8m", "10s").Should(applicationFixture.HaveHealthStatusCode(health.HealthStatusHealthy), "Application did not reach healthy status within timeout") + Eventually(app, "8m", "10s").Should(applicationFixture.HaveSyncStatusCode(appv1alpha1.SyncStatusCodeSynced), "Application did not sync within timeout") + + By("creating ImageUpdater CR") + updateStrategy := "semver" + imageUpdater = &imageUpdaterApi.ImageUpdater{ + ObjectMeta: metav1.ObjectMeta{ + Name: "image-updater", + Namespace: ns.Name, + }, + Spec: imageUpdaterApi.ImageUpdaterSpec{ + Namespace: ns.Name, + ApplicationRefs: []imageUpdaterApi.ApplicationRef{ + { + NamePattern: "app*", + Images: []imageUpdaterApi.ImageConfig{ + { + Alias: "guestbook", + ImageName: "quay.io/dkarpele/my-guestbook:~29437546.0", + CommonUpdateSettings: &imageUpdaterApi.CommonUpdateSettings{ + UpdateStrategy: &updateStrategy, + }, + }, + }, + }, + }, + }, + } + Expect(k8sClient.Create(ctx, imageUpdater)).To(Succeed()) + + By("ensuring that the Application image has `29437546.0` version after update") + Eventually(func() string { + err := k8sClient.Get(ctx, client.ObjectKeyFromObject(app), app) + + if err != nil { + GinkgoWriter.Printf("Error getting Application: %v\n", err) + return "" // Let Eventually retry on error + } + + // Nil-safe check: The Kustomize block is only added by the Image Updater after its first run. + // We must check that it and its Images field exist before trying to access them. + if app.Spec.Source.Kustomize != nil && len(app.Spec.Source.Kustomize.Images) > 0 { + imageStr := string(app.Spec.Source.Kustomize.Images[0]) + GinkgoWriter.Printf("Found Kustomize image: %s\n", imageStr) + return imageStr + } + + // Return an empty string to signify the condition is not yet met. + return "" + }, "10m", "10s").Should(Equal("quay.io/dkarpele/my-guestbook:29437546.0"), "Image Updater did not update the Application image within timeout") + }) + }) +}) diff --git a/test/openshift/e2e/ginkgo/sequential/1-008_validate-4.9CI-Failures_test.go b/test/openshift/e2e/ginkgo/sequential/1-008_validate-4.9CI-Failures_test.go index 9bae182b1..eb9997de2 100644 --- a/test/openshift/e2e/ginkgo/sequential/1-008_validate-4.9CI-Failures_test.go +++ b/test/openshift/e2e/ginkgo/sequential/1-008_validate-4.9CI-Failures_test.go @@ -167,8 +167,8 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Expect(k8sClient.Create(ctx, app)).To(Succeed()) By("verifying Argo CD in source-ns is able to deploy to managed namespace target-ns") - Eventually(app, "4m", "5s").Should(applicationFixture.HaveHealthStatusCode(health.HealthStatusHealthy)) - Eventually(app, "4m", "5s").Should(applicationFixture.HaveSyncStatusCode(argocdv1alpha1.SyncStatusCodeSynced)) + Eventually(app, "8m", "10s").Should(applicationFixture.HaveHealthStatusCode(health.HealthStatusHealthy), "Application did not reach healthy status within timeout") + Eventually(app, "8m", "10s").Should(applicationFixture.HaveSyncStatusCode(argocdv1alpha1.SyncStatusCodeSynced), "Application did not sync within timeout") }) diff --git a/test/openshift/e2e/ginkgo/sequential/1-037_validate_applicationset_in_any_namespace_test.go b/test/openshift/e2e/ginkgo/sequential/1-037_validate_applicationset_in_any_namespace_test.go index 46f876cb0..3847fbec3 100644 --- a/test/openshift/e2e/ginkgo/sequential/1-037_validate_applicationset_in_any_namespace_test.go +++ b/test/openshift/e2e/ginkgo/sequential/1-037_validate_applicationset_in_any_namespace_test.go @@ -95,11 +95,11 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Namespace: argoCD.Namespace, }, } - Eventually(appsetDeployment).Should(k8sFixture.ExistByName()) + Eventually(appsetDeployment, "2m", "5s").Should(k8sFixture.ExistByName()) Expect(appsetDeployment).ShouldNot(deploymentFixture.HaveContainerCommandSubstring("--applicationset-namespaces", 0)) Eventually(argoCD, "5m", "5s").Should(argocdFixture.BeAvailable()) - Eventually(argoCD).Should(argocdFixture.HaveApplicationSetControllerStatus("Running")) + Eventually(argoCD, "3m", "5s").Should(argocdFixture.HaveApplicationSetControllerStatus("Running")) // Verifies that the role/rolebindings in the specified namespace are not managed by application controller or appset, in the given namespace expectRoleAndRoleBindingAndNamespaceToNotBeManaged := func(names []string, namespaceName string) { @@ -115,8 +115,8 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Namespace: namespaceName, }, } - Eventually(role).Should(k8sFixture.NotExistByName()) - Consistently(role).Should(k8sFixture.NotExistByName()) + Eventually(role, "2m", "5s").Should(k8sFixture.NotExistByName()) + Consistently(role, "10s", "1s").Should(k8sFixture.NotExistByName()) roleBinding := &rbacv1.RoleBinding{ ObjectMeta: metav1.ObjectMeta{ @@ -124,8 +124,8 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Namespace: namespaceName, }, } - Eventually(roleBinding).Should(k8sFixture.NotExistByName()) - Consistently(roleBinding).Should(k8sFixture.NotExistByName()) + Eventually(roleBinding, "2m", "5s").Should(k8sFixture.NotExistByName()) + Consistently(roleBinding, "10s", "1s").Should(k8sFixture.NotExistByName()) } @@ -136,8 +136,8 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { } By("verifying that namespace" + namespaceName + " does not have label 'argocd.argoproj.io/applicationset-managed-by-cluster-argocd': 'appset-argocd'") - Eventually(nsToCheck).ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", "appset-argocd")) - Consistently(nsToCheck).ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", "appset-argocd")) + Eventually(nsToCheck, "2m", "5s").ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", "appset-argocd")) + Consistently(nsToCheck, "10s", "1s").ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", "appset-argocd")) } @@ -168,8 +168,8 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Namespace: argoCD.Namespace, }, } - Eventually(appsetDeployment).Should(k8sFixture.ExistByName()) - Eventually(appsetDeployment).ShouldNot(deploymentFixture.HaveContainerCommandSubstring("--applicationset-namespaces", 0)) + Eventually(appsetDeployment, "2m", "5s").Should(k8sFixture.ExistByName()) + Eventually(appsetDeployment, "2m", "5s").ShouldNot(deploymentFixture.HaveContainerCommandSubstring("--applicationset-namespaces", 0)) expectRoleAndRoleBindingAndNamespaceToNotBeManaged([]string{"example_appset-old-ns", "example-appset-argocd-applicationset"}, appset_old_nsNS.Name) @@ -216,16 +216,16 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { }) By("verifying appset namespaces parameter exists, and it points to only the namespace specified in .spec.sourceNamespaces") - Eventually(appsetDeployment).Should(k8sFixture.ExistByName()) - Eventually(appsetDeployment).Should(deploymentFixture.HaveContainerCommandSubstring("--applicationset-namespaces appset-new-ns", 0)) + Eventually(appsetDeployment, "2m", "5s").Should(k8sFixture.ExistByName()) + Eventually(appsetDeployment, "2m", "5s").Should(deploymentFixture.HaveContainerCommandSubstring("--applicationset-namespaces appset-new-ns", 0)) By("verifying that Role in appset-new-ns has expected RBAC permissions: ability to modify applications, batch, and applicationsets") example_appset_new_nsRole := &rbacv1.Role{ ObjectMeta: metav1.ObjectMeta{Name: "example_appset-new-ns", Namespace: appset_new_nsNS.Name}, } - Eventually(example_appset_new_nsRole).Should(k8sFixture.ExistByName()) + Eventually(example_appset_new_nsRole, "2m", "5s").Should(k8sFixture.ExistByName()) - Eventually(example_appset_new_nsRole).Should(roleFixture.HaveRules([]rbacv1.PolicyRule{ + Eventually(example_appset_new_nsRole, "2m", "5s").Should(roleFixture.HaveRules([]rbacv1.PolicyRule{ { APIGroups: []string{"argoproj.io"}, Resources: []string{"applications"}, @@ -261,7 +261,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Namespace: appset_new_nsNS.Name, }, } - Eventually(example_appset_new_nsRoleBinding).Should(k8sFixture.ExistByName()) + Eventually(example_appset_new_nsRoleBinding, "2m", "5s").Should(k8sFixture.ExistByName()) Expect(example_appset_new_nsRoleBinding.RoleRef).To(Equal(rbacv1.RoleRef{ APIGroup: "rbac.authorization.k8s.io", @@ -287,7 +287,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Namespace: "appset-new-ns", }, } - Eventually(example_appset_argocd_applicationsetRole).Should(k8sFixture.ExistByName()) + Eventually(example_appset_argocd_applicationsetRole, "2m", "5s").Should(k8sFixture.ExistByName()) example_appset_argocd_applicationsetRoleBinding := &rbacv1.RoleBinding{ ObjectMeta: metav1.ObjectMeta{ @@ -295,13 +295,13 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Namespace: "appset-new-ns", }, } - Eventually(example_appset_argocd_applicationsetRoleBinding).Should(k8sFixture.ExistByName()) + Eventually(example_appset_argocd_applicationsetRoleBinding, "2m", "5s").Should(k8sFixture.ExistByName()) By("verifying appset-new-ns namespace is managed as both a source namespace and an application set source namespace") - Eventually(appset_new_nsNS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", "appset-argocd")) + Eventually(appset_new_nsNS, "2m", "5s").Should(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", "appset-argocd")) - Eventually(appset_new_nsNS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "appset-argocd")) + Eventually(appset_new_nsNS, "2m", "5s").Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "appset-argocd")) expectRoleAndRoleBindingAndNamespaceToNotBeManaged([]string{"example_appset-old-ns", "example-appset-argocd-applicationset"}, appset_old_nsNS.Name) @@ -327,8 +327,8 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { } }) - Eventually(appsetDeployment).Should(k8sFixture.ExistByName()) - Eventually(appsetDeployment).Should(deploymentFixture.HaveContainerCommandSubstring("--applicationset-namespaces appset-new-ns,appset-old-ns", 0)) + Eventually(appsetDeployment, "2m", "5s").Should(k8sFixture.ExistByName()) + Eventually(appsetDeployment, "2m", "5s").Should(deploymentFixture.HaveContainerCommandSubstring("--applicationset-namespaces appset-new-ns,appset-old-ns", 0)) By("verifying that appset-old-ns gains Role/RoleBindings similar to appset-new-ns") example_appset_old_nsRole := &rbacv1.Role{ @@ -338,7 +338,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { }, } - Eventually(example_appset_old_nsRole).Should(roleFixture.HaveRules([]rbacv1.PolicyRule{ + Eventually(example_appset_old_nsRole, "2m", "5s").Should(roleFixture.HaveRules([]rbacv1.PolicyRule{ { APIGroups: []string{"argoproj.io"}, Resources: []string{"applications"}, @@ -376,7 +376,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { }, } - Eventually(example_appset_old_nsRoleBinding).Should(k8sFixture.ExistByName()) + Eventually(example_appset_old_nsRoleBinding, "2m", "5s").Should(k8sFixture.ExistByName()) Expect(example_appset_old_nsRoleBinding.RoleRef).To(Equal(rbacv1.RoleRef{ APIGroup: "rbac.authorization.k8s.io", @@ -402,7 +402,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Namespace: "appset-old-ns", }, } - Eventually(oldExample_appset_argocd_applicationsetRole).Should(k8sFixture.ExistByName()) + Eventually(oldExample_appset_argocd_applicationsetRole, "2m", "5s").Should(k8sFixture.ExistByName()) oldExample_appset_argocd_applicationsetRoleBinding := &rbacv1.RoleBinding{ ObjectMeta: metav1.ObjectMeta{ @@ -410,14 +410,14 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Namespace: "appset-old-ns", }, } - Eventually(oldExample_appset_argocd_applicationsetRoleBinding).Should(k8sFixture.ExistByName()) + Eventually(oldExample_appset_argocd_applicationsetRoleBinding, "2m", "5s").Should(k8sFixture.ExistByName()) - Eventually(appset_old_nsNS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", "appset-argocd")) - Consistently(appset_old_nsNS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "appset-argocd")) + Eventually(appset_old_nsNS, "2m", "5s").Should(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", "appset-argocd")) + Consistently(appset_old_nsNS, "10s", "1s").Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "appset-argocd")) - Eventually(example_appset_new_nsRole).Should(k8sFixture.ExistByName()) + Eventually(example_appset_new_nsRole, "2m", "5s").Should(k8sFixture.ExistByName()) - Eventually(example_appset_new_nsRole).Should(roleFixture.HaveRules([]rbacv1.PolicyRule{ + Eventually(example_appset_new_nsRole, "2m", "5s").Should(roleFixture.HaveRules([]rbacv1.PolicyRule{ { APIGroups: []string{"argoproj.io"}, Resources: []string{"applications"}, @@ -448,7 +448,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { }, })) - Eventually(example_appset_new_nsRoleBinding).Should(k8sFixture.ExistByName()) + Eventually(example_appset_new_nsRoleBinding, "2m", "5s").Should(k8sFixture.ExistByName()) Expect(example_appset_new_nsRoleBinding.RoleRef).To(Equal(rbacv1.RoleRef{ APIGroup: "rbac.authorization.k8s.io", Kind: "Role", @@ -467,12 +467,12 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { }, })) - Eventually(example_appset_argocd_applicationsetRole).Should(k8sFixture.ExistByName()) - Consistently(example_appset_argocd_applicationsetRole).Should(k8sFixture.ExistByName()) + Eventually(example_appset_argocd_applicationsetRole, "2m", "5s").Should(k8sFixture.ExistByName()) + Consistently(example_appset_argocd_applicationsetRole, "10s", "1s").Should(k8sFixture.ExistByName()) - Eventually(appset_new_nsNS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "appset-argocd")) + Eventually(appset_new_nsNS, "2m", "5s").Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "appset-argocd")) - Eventually(appset_new_nsNS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", "appset-argocd")) + Eventually(appset_new_nsNS, "2m", "5s").Should(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", "appset-argocd")) /// ------------- @@ -497,8 +497,8 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { }) By("verifying that applicationsets has been removed from Role") - Eventually(example_appset_new_nsRole).Should(k8sFixture.ExistByName()) - Eventually(example_appset_new_nsRole).Should(roleFixture.HaveRules([]rbacv1.PolicyRule{ + Eventually(example_appset_new_nsRole, "2m", "5s").Should(k8sFixture.ExistByName()) + Eventually(example_appset_new_nsRole, "2m", "5s").Should(roleFixture.HaveRules([]rbacv1.PolicyRule{ { APIGroups: []string{"argoproj.io"}, Resources: []string{"applications"}, @@ -515,7 +515,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { })) By("verifying RoleBinding still has expected role and subjects") - Eventually(example_appset_new_nsRoleBinding).Should(k8sFixture.ExistByName()) + Eventually(example_appset_new_nsRoleBinding, "2m", "5s").Should(k8sFixture.ExistByName()) Expect(example_appset_new_nsRoleBinding.RoleRef).To(Equal(rbacv1.RoleRef{ APIGroup: "rbac.authorization.k8s.io", Kind: "Role", @@ -535,18 +535,18 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { })) By("verifying appset-new-ns namespace should still be managed-by-cluster-argocd") - Eventually(appset_new_nsNS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "appset-argocd")) + Eventually(appset_new_nsNS, "2m", "5s").Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "appset-argocd")) By("verifying appset-new-ns applicationset role/binding no longer exists in the namespace") - Eventually(example_appset_argocd_applicationsetRole).Should(k8sFixture.NotExistByName()) - Consistently(example_appset_argocd_applicationsetRole).Should(k8sFixture.NotExistByName()) + Eventually(example_appset_argocd_applicationsetRole, "2m", "5s").Should(k8sFixture.NotExistByName()) + Consistently(example_appset_argocd_applicationsetRole, "10s", "1s").Should(k8sFixture.NotExistByName()) - Eventually(example_appset_argocd_applicationsetRoleBinding).Should(k8sFixture.NotExistByName()) - Consistently(example_appset_argocd_applicationsetRoleBinding).Should(k8sFixture.NotExistByName()) + Eventually(example_appset_argocd_applicationsetRoleBinding, "2m", "5s").Should(k8sFixture.NotExistByName()) + Consistently(example_appset_argocd_applicationsetRoleBinding, "10s", "1s").Should(k8sFixture.NotExistByName()) By("verifying appset-new-ns applicationset is not applicationset-managed-by Argo CD instance") - Eventually(appset_new_nsNS).ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", "appset-argocd")) - Consistently(appset_new_nsNS).ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", "appset-argocd")) + Eventually(appset_new_nsNS, "2m", "5s").ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", "appset-argocd")) + Consistently(appset_new_nsNS, "10s", "1s").ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", "appset-argocd")) // --- @@ -566,30 +566,30 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { }) By("verifying role/rolebinding no longer exists in any namespace") - Eventually(example_appset_new_nsRole).Should(k8sFixture.NotExistByName()) - Consistently(example_appset_new_nsRole).Should(k8sFixture.NotExistByName()) - Eventually(example_appset_new_nsRoleBinding).Should(k8sFixture.NotExistByName()) - Consistently(example_appset_new_nsRoleBinding).Should(k8sFixture.NotExistByName()) + Eventually(example_appset_new_nsRole, "2m", "5s").Should(k8sFixture.NotExistByName()) + Consistently(example_appset_new_nsRole, "10s", "1s").Should(k8sFixture.NotExistByName()) + Eventually(example_appset_new_nsRoleBinding, "2m", "5s").Should(k8sFixture.NotExistByName()) + Consistently(example_appset_new_nsRoleBinding, "10s", "1s").Should(k8sFixture.NotExistByName()) - Eventually(example_appset_old_nsRole).Should(k8sFixture.NotExistByName()) - Consistently(example_appset_old_nsRole).Should(k8sFixture.NotExistByName()) - Eventually(example_appset_old_nsRoleBinding).Should(k8sFixture.NotExistByName()) - Consistently(example_appset_old_nsRoleBinding).Should(k8sFixture.NotExistByName()) + Eventually(example_appset_old_nsRole, "2m", "5s").Should(k8sFixture.NotExistByName()) + Consistently(example_appset_old_nsRole, "10s", "1s").Should(k8sFixture.NotExistByName()) + Eventually(example_appset_old_nsRoleBinding, "2m", "5s").Should(k8sFixture.NotExistByName()) + Consistently(example_appset_old_nsRoleBinding, "10s", "1s").Should(k8sFixture.NotExistByName()) - Eventually(oldExample_appset_argocd_applicationsetRole).Should(k8sFixture.NotExistByName()) - Consistently(oldExample_appset_argocd_applicationsetRole).Should(k8sFixture.NotExistByName()) - Eventually(oldExample_appset_argocd_applicationsetRoleBinding).Should(k8sFixture.NotExistByName()) - Consistently(oldExample_appset_argocd_applicationsetRoleBinding).Should(k8sFixture.NotExistByName()) + Eventually(oldExample_appset_argocd_applicationsetRole, "2m", "5s").Should(k8sFixture.NotExistByName()) + Consistently(oldExample_appset_argocd_applicationsetRole, "10s", "1s").Should(k8sFixture.NotExistByName()) + Eventually(oldExample_appset_argocd_applicationsetRoleBinding, "2m", "5s").Should(k8sFixture.NotExistByName()) + Consistently(oldExample_appset_argocd_applicationsetRoleBinding, "10s", "1s").Should(k8sFixture.NotExistByName()) By("verifying applicationset-managed-by and managed-by are not set on any namespace") - Eventually(appset_old_nsNS).ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", "appset-argocd")) - Consistently(appset_old_nsNS).ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", "appset-argocd")) + Eventually(appset_old_nsNS, "2m", "5s").ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", "appset-argocd")) + Consistently(appset_old_nsNS, "10s", "1s").ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", "appset-argocd")) - Eventually(appset_old_nsNS).ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "appset-argocd")) - Consistently(appset_old_nsNS).ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "appset-argocd")) + Eventually(appset_old_nsNS, "2m", "5s").ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "appset-argocd")) + Consistently(appset_old_nsNS, "10s", "1s").ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "appset-argocd")) - Eventually(appset_new_nsNS).ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "appset-argocd")) - Consistently(appset_new_nsNS).ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "appset-argocd")) + Eventually(appset_new_nsNS, "2m", "5s").ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "appset-argocd")) + Consistently(appset_new_nsNS, "10s", "1s").ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "appset-argocd")) }) @@ -616,7 +616,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { }, } Expect(k8sClient.Create(ctx, argoCD)).To(Succeed()) - Eventually(argoCD).Should(argocdFixture.HaveApplicationSetControllerStatus("Running")) + Eventually(argoCD, "3m", "5s").Should(argocdFixture.HaveApplicationSetControllerStatus("Running")) By("verifying that the appset deplomyent does not contain 'applications in any namespace' parameter") appsetDeployment := &appsv1.Deployment{ @@ -625,7 +625,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Namespace: argoCD.Namespace, }, } - Eventually(appsetDeployment).Should(k8sFixture.ExistByName()) + Eventually(appsetDeployment, "2m", "5s").Should(k8sFixture.ExistByName()) Expect(appsetDeployment).ShouldNot(deploymentFixture.HaveContainerCommandSubstring("--applicationset-namespaces", 0)) By("first verify that the ClusterRole was not automatically created for the Argo CD instance") @@ -635,11 +635,11 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Annotations: common.DefaultAnnotations(argoCD.Name, argoCD.Namespace), }, } - Consistently(clusterRole).Should(k8sFixture.NotExistByName()) + Consistently(clusterRole, "10s", "1s").Should(k8sFixture.NotExistByName()) By("creating ClusterRole and then ensuring it is automatically cleaned up") Expect(k8sClient.Create(ctx, clusterRole)).To(Succeed()) - Eventually(clusterRole).ShouldNot(k8sFixture.ExistByName()) + Eventually(clusterRole, "2m", "5s").ShouldNot(k8sFixture.ExistByName()) By("first verify that ClusterRoleBinding was not automatically created for the Argo CD instance") clusterRoleBinding := &rbacv1.ClusterRoleBinding{ @@ -660,10 +660,10 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Name: clusterRole.Name, }, } - Consistently(clusterRoleBinding).Should(k8sFixture.NotExistByName()) + Consistently(clusterRoleBinding, "10s", "1s").Should(k8sFixture.NotExistByName()) By("creating ClusterRoleBinding and then ensuring it is automatically cleaned up") Expect(k8sClient.Create(ctx, clusterRoleBinding)).To(Succeed()) - Eventually(clusterRoleBinding).ShouldNot(k8sFixture.ExistByName()) + Eventually(clusterRoleBinding, "2m", "5s").ShouldNot(k8sFixture.ExistByName()) By("first verifying that Role does not exist in namespace specified in appset sourceNamespaces field") roleInTargetNS := &rbacv1.Role{ @@ -672,10 +672,10 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Namespace: targetNS.Name, }, } - Consistently(roleInTargetNS).Should(k8sFixture.NotExistByName()) + Consistently(roleInTargetNS, "10s", "1s").Should(k8sFixture.NotExistByName()) By("creating Role in source NS and verifying it is not cleaned up (yet)") Expect(k8sClient.Create(ctx, roleInTargetNS)).To(Succeed()) - Consistently(roleInTargetNS).Should(k8sFixture.ExistByName()) + Consistently(roleInTargetNS, "10s", "1s").Should(k8sFixture.ExistByName()) By("verifying that there exist no rolebindings that point to the namespace-scoped argocd instance namespace") Consistently(func() bool { @@ -715,10 +715,10 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Name: roleInTargetNS.Name, }, } - Consistently(roleBindingInTargetNS).Should(k8sFixture.NotExistByName()) + Consistently(roleBindingInTargetNS, "10s", "1s").Should(k8sFixture.NotExistByName()) By("creating RoleBinding in source NS and verifying it is not cleaned up (yet)") Expect(k8sClient.Create(ctx, roleBindingInTargetNS)).To(Succeed()) - Consistently(roleBindingInTargetNS).Should(k8sFixture.ExistByName()) + Consistently(roleBindingInTargetNS, "10s", "1s").Should(k8sFixture.ExistByName()) By("adding ArgoCDApplicationSetManagedByClusterArgoCDLabel label to target NS") namespaceFixture.Update(targetNS, func(n *corev1.Namespace) { @@ -729,11 +729,11 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { }) By("verifying the label is automatically removed") - Eventually(targetNS).Should(k8sFixture.NotHaveLabelWithValue(common.ArgoCDApplicationSetManagedByClusterArgoCDLabel, argoCD.Namespace)) + Eventually(targetNS, "2m", "5s").Should(k8sFixture.NotHaveLabelWithValue(common.ArgoCDApplicationSetManagedByClusterArgoCDLabel, argoCD.Namespace)) By("verifying that the roles/rolebindings we created in the previous steps are now automatically cleaned up, because the namespace had the ArgoCDApplicationSetManagedByClusterArgoCDLabel") - Eventually(roleBindingInTargetNS).Should(k8sFixture.NotExistByName()) - Eventually(roleInTargetNS).Should(k8sFixture.NotExistByName()) + Eventually(roleBindingInTargetNS, "2m", "5s").Should(k8sFixture.NotExistByName()) + Eventually(roleInTargetNS, "2m", "5s").Should(k8sFixture.NotExistByName()) }) It("verifies that wildcard patterns in .spec.applicationSet.sourceNamespaces correctly match and manage multiple namespaces", func() { @@ -786,7 +786,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Expect(k8sClient.Create(ctx, argoCD)).To(Succeed()) Eventually(argoCD, "5m", "5s").Should(argocdFixture.BeAvailable()) - Eventually(argoCD).Should(argocdFixture.HaveApplicationSetControllerStatus("Running")) + Eventually(argoCD, "3m", "5s").Should(argocdFixture.HaveApplicationSetControllerStatus("Running")) By("2) verifying that the appset deployment contains all matching namespaces in the command") appsetDeployment := &appsv1.Deployment{ @@ -795,10 +795,10 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Namespace: argoCD.Namespace, }, } - Eventually(appsetDeployment).Should(k8sFixture.ExistByName()) + Eventually(appsetDeployment, "2m", "5s").Should(k8sFixture.ExistByName()) // Verify that all team-* namespaces are included (order may vary) - Eventually(appsetDeployment).Should(deploymentFixture.HaveContainerCommandSubstring("--applicationset-namespaces", 0)) + Eventually(appsetDeployment, "2m", "5s").Should(deploymentFixture.HaveContainerCommandSubstring("--applicationset-namespaces", 0)) Eventually(func() bool { if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(appsetDeployment), appsetDeployment); err != nil { return false @@ -816,7 +816,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { strings.Contains(cmdStr, "team-backend") } return false - }).Should(BeTrue()) + }, "3m", "5s").Should(BeTrue(), "Deployment command did not contain all expected team-* namespaces within timeout") By("3) verifying that Role and RoleBinding are created in all matching team-* namespaces") verifyAppSetResourcesInNamespace := func(namespaceName string) { @@ -826,7 +826,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Namespace: namespaceName, }, } - Eventually(appsetRole).Should(k8sFixture.ExistByName()) + Eventually(appsetRole, "2m", "5s").Should(k8sFixture.ExistByName()) appsetRoleBinding := &rbacv1.RoleBinding{ ObjectMeta: metav1.ObjectMeta{ @@ -834,7 +834,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Namespace: namespaceName, }, } - Eventually(appsetRoleBinding).Should(k8sFixture.ExistByName()) + Eventually(appsetRoleBinding, "2m", "5s").Should(k8sFixture.ExistByName()) Expect(appsetRoleBinding.RoleRef).To(Equal(rbacv1.RoleRef{ APIGroup: "rbac.authorization.k8s.io", Kind: "Role", @@ -853,10 +853,10 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { verifyAppSetResourcesInNamespace(teamBackendNS.Name) By("4) verifying that namespace labels are set correctly for all matching namespaces") - Eventually(team1NS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", appset_wildcard_argocdNS.Name)) - Eventually(team2NS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", appset_wildcard_argocdNS.Name)) - Eventually(teamFrontendNS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", appset_wildcard_argocdNS.Name)) - Eventually(teamBackendNS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", appset_wildcard_argocdNS.Name)) + Eventually(team1NS, "2m", "5s").Should(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", appset_wildcard_argocdNS.Name)) + Eventually(team2NS, "2m", "5s").Should(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", appset_wildcard_argocdNS.Name)) + Eventually(teamFrontendNS, "2m", "5s").Should(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", appset_wildcard_argocdNS.Name)) + Eventually(teamBackendNS, "2m", "5s").Should(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", appset_wildcard_argocdNS.Name)) By("5) verifying that non-matching namespace (other-ns) does NOT have appset resources") otherNSAppSetRole := &rbacv1.Role{ @@ -865,7 +865,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Namespace: otherNS.Name, }, } - Consistently(otherNSAppSetRole).Should(k8sFixture.NotExistByName()) + Consistently(otherNSAppSetRole, "10s", "1s").Should(k8sFixture.NotExistByName()) otherNSAppSetRoleBinding := &rbacv1.RoleBinding{ ObjectMeta: metav1.ObjectMeta{ @@ -873,9 +873,9 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Namespace: otherNS.Name, }, } - Consistently(otherNSAppSetRoleBinding).Should(k8sFixture.NotExistByName()) + Consistently(otherNSAppSetRoleBinding, "10s", "1s").Should(k8sFixture.NotExistByName()) - Consistently(otherNS).ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", appset_wildcard_argocdNS.Name)) + Consistently(otherNS, "10s", "1s").ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", appset_wildcard_argocdNS.Name)) By("6) creating a new namespace that matches the pattern and verifying it gets resources automatically") team3NS, cleanupFunc := fixture.CreateNamespaceWithCleanupFunc("team-3") @@ -893,7 +893,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { }, "2m", "5s").Should(BeTrue()) verifyAppSetResourcesInNamespace(team3NS.Name) - Eventually(team3NS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", appset_wildcard_argocdNS.Name)) + Eventually(team3NS, "2m", "5s").Should(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", appset_wildcard_argocdNS.Name)) By("7) updating ArgoCD to use a more specific pattern 'team-*' -> 'team-1' and verifying cleanup") argocdFixture.Update(argoCD, func(ac *v1beta1.ArgoCD) { @@ -917,7 +917,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Namespace: team1NS.Name, }, } - Eventually(team1AppSetRole).Should(k8sFixture.ExistByName()) + Eventually(team1AppSetRole, "2m", "5s").Should(k8sFixture.ExistByName()) By("9) verifying that other team-* namespaces have resources cleaned up") team2AppSetRole := &rbacv1.Role{ @@ -926,8 +926,8 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Namespace: team2NS.Name, }, } - Eventually(team2AppSetRole).Should(k8sFixture.NotExistByName()) - Consistently(team2AppSetRole).Should(k8sFixture.NotExistByName()) + Eventually(team2AppSetRole, "2m", "5s").Should(k8sFixture.NotExistByName()) + Consistently(team2AppSetRole, "10s", "1s").Should(k8sFixture.NotExistByName()) team3AppSetRole := &rbacv1.Role{ ObjectMeta: metav1.ObjectMeta{ @@ -935,8 +935,8 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Namespace: team3NS.Name, }, } - Eventually(team3AppSetRole).Should(k8sFixture.NotExistByName()) - Consistently(team3AppSetRole).Should(k8sFixture.NotExistByName()) + Eventually(team3AppSetRole, "2m", "5s").Should(k8sFixture.NotExistByName()) + Consistently(team3AppSetRole, "10s", "1s").Should(k8sFixture.NotExistByName()) teamFrontendAppSetRole := &rbacv1.Role{ ObjectMeta: metav1.ObjectMeta{ @@ -944,13 +944,13 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Namespace: teamFrontendNS.Name, }, } - Eventually(teamFrontendAppSetRole).Should(k8sFixture.NotExistByName()) - Consistently(teamFrontendAppSetRole).Should(k8sFixture.NotExistByName()) + Eventually(teamFrontendAppSetRole, "2m", "5s").Should(k8sFixture.NotExistByName()) + Consistently(teamFrontendAppSetRole, "10s", "1s").Should(k8sFixture.NotExistByName()) By("10) verifying that labels are removed from namespaces that no longer match") - Eventually(team2NS).ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", appset_wildcard_argocdNS.Name)) - Eventually(team3NS).ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", appset_wildcard_argocdNS.Name)) - Eventually(teamFrontendNS).ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", appset_wildcard_argocdNS.Name)) + Eventually(team2NS, "2m", "5s").ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", appset_wildcard_argocdNS.Name)) + Eventually(team3NS, "2m", "5s").ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", appset_wildcard_argocdNS.Name)) + Eventually(teamFrontendNS, "2m", "5s").ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/applicationset-managed-by-cluster-argocd", appset_wildcard_argocdNS.Name)) By("11) verifying deployment command only includes team-1") Eventually(func() bool { @@ -969,7 +969,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { !strings.Contains(cmdStr, "team-frontend") } return false - }).Should(BeTrue()) + }, "3m", "5s").Should(BeTrue(), "Deployment command did not match expected pattern within timeout") }) diff --git a/test/openshift/e2e/ginkgo/sequential/1-058_validate_notifications_source_namespaces_test.go b/test/openshift/e2e/ginkgo/sequential/1-058_validate_notifications_source_namespaces_test.go new file mode 100644 index 000000000..b9dcfba29 --- /dev/null +++ b/test/openshift/e2e/ginkgo/sequential/1-058_validate_notifications_source_namespaces_test.go @@ -0,0 +1,636 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sequential + +import ( + "context" + "strings" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + appsv1 "k8s.io/api/apps/v1" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + argov1alpha1api "github.com/argoproj-labs/argocd-operator/api/v1alpha1" + argov1beta1api "github.com/argoproj-labs/argocd-operator/api/v1beta1" + "github.com/argoproj-labs/argocd-operator/common" + "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture" + argocdFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/argocd" + k8sFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/k8s" + namespaceFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/namespace" + fixtureUtils "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/utils" + + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var _ = Describe("GitOps Operator Sequential E2E Tests", func() { + + Context("1-058_validate_notifications_source_namespaces", func() { + + var ( + k8sClient client.Client + ctx context.Context + ) + + BeforeEach(func() { + fixture.EnsureSequentialCleanSlate() + fixture.SetEnvInOperatorSubscriptionOrDeployment("ARGOCD_CLUSTER_CONFIG_NAMESPACES", "openshift-gitops, argocd-e2e-cluster-config") + k8sClient, _ = fixtureUtils.GetE2ETestKubeClient() + ctx = context.Background() + }) + + AfterEach(func() { + fixture.OutputDebugOnFail("not-argocd-ns") + }) + + It("ensures that NotificationsConfiguration, Role, and RoleBinding are created in source namespaces when notifications.sourceNamespaces is configured", func() { + + By("creating Argo CD instance namespace") + argocdNS, cleanupFunc := fixture.CreateNamespaceWithCleanupFunc("argocd-e2e-cluster-config") + defer cleanupFunc() + + By("creating source namespaces") + sourceNS1, cleanupFunc1 := fixture.CreateNamespaceWithCleanupFunc("notif-source-ns-1") + defer cleanupFunc1() + + sourceNS2, cleanupFunc2 := fixture.CreateNamespaceWithCleanupFunc("notif-source-ns-2") + defer cleanupFunc2() + + By("creating Argo CD instance with notifications enabled and sourceNamespaces configured") + argocd := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-argocd", + Namespace: argocdNS.Name, + }, + Spec: argov1beta1api.ArgoCDSpec{ + SourceNamespaces: []string{sourceNS1.Name, sourceNS2.Name}, + Notifications: argov1beta1api.ArgoCDNotifications{ + Enabled: true, + SourceNamespaces: []string{sourceNS1.Name, sourceNS2.Name}, + }, + }, + } + Expect(k8sClient.Create(ctx, argocd)).To(Succeed()) + + By("waiting for Argo CD to be available") + Eventually(argocd, "5m", "5s").Should(argocdFixture.BeAvailable()) + + By("verifying notification controller is running") + Eventually(argocd, "4m", "5s").Should(argocdFixture.HaveNotificationControllerStatus("Running")) + + By("verifying NotificationsConfiguration CR is created in source namespace 1") + notifCfg1 := &argov1alpha1api.NotificationsConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default-notifications-configuration", + Namespace: sourceNS1.Name, + }, + } + Eventually(notifCfg1).Should(k8sFixture.ExistByName()) + + By("verifying NotificationsConfiguration CR is created in source namespace 2") + notifCfg2 := &argov1alpha1api.NotificationsConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default-notifications-configuration", + Namespace: sourceNS2.Name, + }, + } + Eventually(notifCfg2).Should(k8sFixture.ExistByName()) + + By("verifying Role is created in source namespace 1") + roleName1 := "example-argocd-" + argocdNS.Name + "-notifications" + role1 := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleName1, + Namespace: sourceNS1.Name, + }, + } + Eventually(role1).Should(k8sFixture.ExistByName()) + + By("verifying RoleBinding is created in source namespace 1") + roleBinding1 := &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleName1, + Namespace: sourceNS1.Name, + }, + } + Eventually(roleBinding1).Should(k8sFixture.ExistByName()) + + By("verifying namespace 1 has the notifications-managed-by-cluster-argocd label") + Eventually(sourceNS1).Should(namespaceFixture.HaveLabel(common.ArgoCDNotificationsManagedByClusterArgoCDLabel, argocdNS.Name)) + + By("verifying namespace 2 has the notifications-managed-by-cluster-argocd label") + Eventually(sourceNS2).Should(namespaceFixture.HaveLabel(common.ArgoCDNotificationsManagedByClusterArgoCDLabel, argocdNS.Name)) + + By("verifying notifications controller deployment has --application-namespaces and --self-service-notification-enabled flags") + notifDepl := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-argocd-notifications-controller", + Namespace: argocdNS.Name, + }, + } + Eventually(func() bool { + err := k8sClient.Get(ctx, client.ObjectKeyFromObject(notifDepl), notifDepl) + if err != nil { + return false + } + if len(notifDepl.Spec.Template.Spec.Containers) == 0 { + return false + } + cmd := notifDepl.Spec.Template.Spec.Containers[0].Command + cmdStr := strings.Join(cmd, " ") + hasAppNamespaces := strings.Contains(cmdStr, "--application-namespaces") + hasSelfService := strings.Contains(cmdStr, "--self-service-notification-enabled") + hasBothNamespaces := strings.Contains(cmdStr, sourceNS1.Name) && strings.Contains(cmdStr, sourceNS2.Name) + return hasAppNamespaces && hasSelfService && hasBothNamespaces + }, "2m", "5s").Should(BeTrue()) + + By("verifying ClusterRole is created for notifications controller") + notifClusterRole := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-argocd-" + argocdNS.Name + "-argocd-notifications-controller", + }, + } + Eventually(notifClusterRole).Should(k8sFixture.ExistByName()) + + By("verifying ClusterRoleBinding is created for notifications controller") + notifClusterRoleBinding := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-argocd-" + argocdNS.Name + "-argocd-notifications-controller", + }, + } + Eventually(notifClusterRoleBinding).Should(k8sFixture.ExistByName()) + + By("verifying ClusterRoleBinding references the correct ClusterRole and ServiceAccount") + Eventually(func() bool { + err := k8sClient.Get(ctx, client.ObjectKeyFromObject(notifClusterRoleBinding), notifClusterRoleBinding) + if err != nil { + return false + } + expectedRoleRef := rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: notifClusterRole.Name, + } + expectedSubject := rbacv1.Subject{ + Kind: "ServiceAccount", + Name: "example-argocd-argocd-notifications-controller", + Namespace: argocdNS.Name, + } + return notifClusterRoleBinding.RoleRef == expectedRoleRef && + len(notifClusterRoleBinding.Subjects) == 1 && + notifClusterRoleBinding.Subjects[0] == expectedSubject + }, "2m", "5s").Should(BeTrue()) + + }) + + It("ensures that resources are not created when namespace is not in SourceNamespaces", func() { + + By("creating Argo CD instance namespace") + argocdNS, cleanupFunc := fixture.CreateNamespaceWithCleanupFunc("argocd-e2e-cluster-config") + defer cleanupFunc() + + By("creating source namespaces") + sourceNS1, cleanupFunc1 := fixture.CreateNamespaceWithCleanupFunc("notif-source-ns-3") + defer cleanupFunc1() + + unmanagedNS, cleanupFunc2 := fixture.CreateNamespaceWithCleanupFunc("notif-unmanaged-ns") + defer cleanupFunc2() + + By("creating Argo CD instance with notifications enabled but only sourceNS1 in both SourceNamespaces and Notifications.SourceNamespaces") + argocd := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-argocd", + Namespace: argocdNS.Name, + }, + Spec: argov1beta1api.ArgoCDSpec{ + SourceNamespaces: []string{sourceNS1.Name}, + Notifications: argov1beta1api.ArgoCDNotifications{ + Enabled: true, + SourceNamespaces: []string{sourceNS1.Name, unmanagedNS.Name}, + }, + }, + } + Expect(k8sClient.Create(ctx, argocd)).To(Succeed()) + + By("waiting for Argo CD to be available") + Eventually(argocd, "5m", "5s").Should(argocdFixture.BeAvailable()) + + fixture.OutputDebugOnFail(argocdNS.Name) + + By("verifying NotificationsConfiguration CR is created in sourceNS1") + notifCfg1 := &argov1alpha1api.NotificationsConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default-notifications-configuration", + Namespace: sourceNS1.Name, + }, + } + Eventually(notifCfg1).Should(k8sFixture.ExistByName()) + + By("verifying NotificationsConfiguration CR is NOT created in unmanagedNS") + notifCfgUnmanaged := &argov1alpha1api.NotificationsConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default-notifications-configuration", + Namespace: unmanagedNS.Name, + }, + } + Consistently(notifCfgUnmanaged).Should(k8sFixture.NotExistByName()) + + By("verifying Role is NOT created in unmanagedNS") + roleName := "example-argocd-" + argocdNS.Name + "-notifications" + roleUnmanaged := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleName, + Namespace: unmanagedNS.Name, + }, + } + Consistently(roleUnmanaged).Should(k8sFixture.NotExistByName()) + + By("verifying unmanagedNS does not have the notifications-managed-by-cluster-argocd label") + Consistently(unmanagedNS).ShouldNot(namespaceFixture.HaveLabel(common.ArgoCDNotificationsManagedByClusterArgoCDLabel, argocdNS.Name)) + + By("verifying notifications controller deployment command only includes sourceNS1") + notifDepl := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-argocd-notifications-controller", + Namespace: argocdNS.Name, + }, + } + Eventually(func() bool { + err := k8sClient.Get(ctx, client.ObjectKeyFromObject(notifDepl), notifDepl) + if err != nil { + return false + } + if len(notifDepl.Spec.Template.Spec.Containers) == 0 { + return false + } + cmd := notifDepl.Spec.Template.Spec.Containers[0].Command + cmdStr := strings.Join(cmd, " ") + hasSourceNS1 := strings.Contains(cmdStr, sourceNS1.Name) + hasUnmanagedNS := strings.Contains(cmdStr, unmanagedNS.Name) + return hasSourceNS1 && !hasUnmanagedNS + }, "2m", "5s").Should(BeTrue()) + + }) + + It("ensures that resources are cleaned up when sourceNamespaces are removed", func() { + + By("creating Argo CD instance namespace") + argocdNS, cleanupFunc := fixture.CreateNamespaceWithCleanupFunc("argocd-e2e-cluster-config") + defer cleanupFunc() + + By("creating source namespaces") + sourceNS1, cleanupFunc1 := fixture.CreateNamespaceWithCleanupFunc("notif-source-ns-4") + defer cleanupFunc1() + + sourceNS2, cleanupFunc2 := fixture.CreateNamespaceWithCleanupFunc("notif-source-ns-5") + defer cleanupFunc2() + + By("creating Argo CD instance with notifications enabled and both namespaces configured") + argocd := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-argocd", + Namespace: argocdNS.Name, + }, + Spec: argov1beta1api.ArgoCDSpec{ + SourceNamespaces: []string{sourceNS1.Name, sourceNS2.Name}, + Notifications: argov1beta1api.ArgoCDNotifications{ + Enabled: true, + SourceNamespaces: []string{sourceNS1.Name, sourceNS2.Name}, + }, + }, + } + Expect(k8sClient.Create(ctx, argocd)).To(Succeed()) + + By("waiting for Argo CD to be available") + Eventually(argocd, "5m", "5s").Should(argocdFixture.BeAvailable()) + + By("verifying resources are created in both namespaces") + roleName := "example-argocd-" + argocdNS.Name + "-notifications" + notifCfg1 := &argov1alpha1api.NotificationsConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default-notifications-configuration", + Namespace: sourceNS1.Name, + }, + } + Eventually(notifCfg1).Should(k8sFixture.ExistByName()) + + notifCfg2 := &argov1alpha1api.NotificationsConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default-notifications-configuration", + Namespace: sourceNS2.Name, + }, + } + Eventually(notifCfg2).Should(k8sFixture.ExistByName()) + + role1 := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleName, + Namespace: sourceNS1.Name, + }, + } + Eventually(role1).Should(k8sFixture.ExistByName()) + + role2 := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleName, + Namespace: sourceNS2.Name, + }, + } + Eventually(role2).Should(k8sFixture.ExistByName()) + + By("removing sourceNS1 from Notifications.SourceNamespaces") + argocdFixture.Update(argocd, func(ac *argov1beta1api.ArgoCD) { + ac.Spec.Notifications.SourceNamespaces = []string{sourceNS2.Name} + }) + + By("waiting for Argo CD to reconcile") + Eventually(argocd, "2m", "5s").Should(argocdFixture.BeAvailable()) + + By("verifying resources are removed from sourceNS1") + Eventually(notifCfg1, "3m", "5s").Should(k8sFixture.NotExistByName()) + Eventually(role1, "3m", "5s").Should(k8sFixture.NotExistByName()) + + roleBinding1 := &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleName, + Namespace: sourceNS1.Name, + }, + } + Eventually(roleBinding1, "3m", "5s").Should(k8sFixture.NotExistByName()) + + By("verifying sourceNS1 no longer has the notifications-managed-by-cluster-argocd label") + Eventually(sourceNS1, "2m", "5s").ShouldNot(namespaceFixture.HaveLabel(common.ArgoCDNotificationsManagedByClusterArgoCDLabel, argocdNS.Name)) + + By("verifying resources still exist in sourceNS2") + Consistently(notifCfg2).Should(k8sFixture.ExistByName()) + Consistently(role2).Should(k8sFixture.ExistByName()) + + roleBinding2 := &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleName, + Namespace: sourceNS2.Name, + }, + } + Consistently(roleBinding2).Should(k8sFixture.ExistByName()) + + By("verifying sourceNS2 still has the notifications-managed-by-cluster-argocd label") + Consistently(sourceNS2).Should(namespaceFixture.HaveLabel(common.ArgoCDNotificationsManagedByClusterArgoCDLabel, argocdNS.Name)) + + }) + + It("ensures that resources are not created when notifications are disabled", func() { + + By("creating Argo CD instance namespace") + argocdNS, cleanupFunc := fixture.CreateNamespaceWithCleanupFunc("argocd-e2e-cluster-config") + defer cleanupFunc() + + By("creating source namespace") + sourceNS1, cleanupFunc1 := fixture.CreateNamespaceWithCleanupFunc("notif-source-ns-6") + defer cleanupFunc1() + + By("creating Argo CD instance with notifications disabled but sourceNamespaces configured") + argocd := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-argocd", + Namespace: argocdNS.Name, + }, + Spec: argov1beta1api.ArgoCDSpec{ + SourceNamespaces: []string{sourceNS1.Name}, + Notifications: argov1beta1api.ArgoCDNotifications{ + Enabled: false, + SourceNamespaces: []string{sourceNS1.Name}, + }, + }, + } + Expect(k8sClient.Create(ctx, argocd)).To(Succeed()) + + By("waiting for Argo CD to be available") + Eventually(argocd, "5m", "5s").Should(argocdFixture.BeAvailable()) + + By("verifying NotificationsConfiguration CR is NOT created in source namespace") + notifCfg := &argov1alpha1api.NotificationsConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default-notifications-configuration", + Namespace: sourceNS1.Name, + }, + } + Consistently(notifCfg).Should(k8sFixture.NotExistByName()) + + By("verifying Role is NOT created in source namespace") + roleName := "example-argocd-" + argocdNS.Name + "-notifications" + role := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleName, + Namespace: sourceNS1.Name, + }, + } + Consistently(role).Should(k8sFixture.NotExistByName()) + + By("verifying ClusterRole is NOT created for notifications controller") + notifClusterRole := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-argocd-" + argocdNS.Name + "-argocd-notifications-controller", + }, + } + Consistently(notifClusterRole).Should(k8sFixture.NotExistByName()) + + By("verifying ClusterRoleBinding is NOT created for notifications controller") + notifClusterRoleBinding := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-argocd-" + argocdNS.Name + "-argocd-notifications-controller", + }, + } + Consistently(notifClusterRoleBinding).Should(k8sFixture.NotExistByName()) + + By("verifying source namespace does not have the notifications-managed-by-cluster-argocd label") + Consistently(sourceNS1).ShouldNot(namespaceFixture.HaveLabel(common.ArgoCDNotificationsManagedByClusterArgoCDLabel, argocdNS.Name)) + + }) + + It("ensures that notifications controller deployment command is updated when sourceNamespaces change", func() { + + By("creating Argo CD instance namespace") + argocdNS, cleanupFunc := fixture.CreateNamespaceWithCleanupFunc("argocd-e2e-cluster-config") + defer cleanupFunc() + + By("creating source namespaces") + sourceNS1, cleanupFunc1 := fixture.CreateNamespaceWithCleanupFunc("notif-source-ns-7") + defer cleanupFunc1() + + sourceNS2, cleanupFunc2 := fixture.CreateNamespaceWithCleanupFunc("notif-source-ns-8") + defer cleanupFunc2() + + By("creating Argo CD instance with notifications enabled and only sourceNS1 configured") + argocd := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-argocd", + Namespace: argocdNS.Name, + }, + Spec: argov1beta1api.ArgoCDSpec{ + SourceNamespaces: []string{sourceNS1.Name, sourceNS2.Name}, + Notifications: argov1beta1api.ArgoCDNotifications{ + Enabled: true, + SourceNamespaces: []string{sourceNS1.Name}, + }, + }, + } + Expect(k8sClient.Create(ctx, argocd)).To(Succeed()) + + By("waiting for Argo CD to be available") + Eventually(argocd, "5m", "5s").Should(argocdFixture.BeAvailable()) + + By("verifying notifications controller deployment command includes only sourceNS1") + notifDepl := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-argocd-notifications-controller", + Namespace: argocdNS.Name, + }, + } + Eventually(func() bool { + err := k8sClient.Get(ctx, client.ObjectKeyFromObject(notifDepl), notifDepl) + if err != nil { + return false + } + if len(notifDepl.Spec.Template.Spec.Containers) == 0 { + return false + } + cmd := notifDepl.Spec.Template.Spec.Containers[0].Command + cmdStr := strings.Join(cmd, " ") + hasSourceNS1 := strings.Contains(cmdStr, sourceNS1.Name) + hasSourceNS2 := strings.Contains(cmdStr, sourceNS2.Name) + return hasSourceNS1 && !hasSourceNS2 + }, "2m", "5s").Should(BeTrue()) + + By("adding sourceNS2 to Notifications.SourceNamespaces") + argocdFixture.Update(argocd, func(ac *argov1beta1api.ArgoCD) { + ac.Spec.Notifications.SourceNamespaces = []string{sourceNS1.Name, sourceNS2.Name} + }) + + By("waiting for Argo CD to reconcile") + Eventually(argocd, "2m", "5s").Should(argocdFixture.BeAvailable()) + + By("verifying notifications controller deployment command now includes both namespaces") + Eventually(func() bool { + err := k8sClient.Get(ctx, client.ObjectKeyFromObject(notifDepl), notifDepl) + if err != nil { + return false + } + if len(notifDepl.Spec.Template.Spec.Containers) == 0 { + return false + } + cmd := notifDepl.Spec.Template.Spec.Containers[0].Command + cmdStr := strings.Join(cmd, " ") + hasSourceNS1 := strings.Contains(cmdStr, sourceNS1.Name) + hasSourceNS2 := strings.Contains(cmdStr, sourceNS2.Name) + hasSelfService := strings.Contains(cmdStr, "--self-service-notification-enabled") + return hasSourceNS1 && hasSourceNS2 && hasSelfService + }, "2m", "5s").Should(BeTrue()) + + }) + + It("ensures that resources are created when notifications are enabled after being disabled", func() { + + By("creating Argo CD instance namespace") + argocdNS, cleanupFunc := fixture.CreateNamespaceWithCleanupFunc("argocd-e2e-cluster-config") + defer cleanupFunc() + + By("creating source namespace") + sourceNS1, cleanupFunc1 := fixture.CreateNamespaceWithCleanupFunc("notif-source-ns-9") + defer cleanupFunc1() + + By("creating Argo CD instance with notifications disabled") + argocd := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-argocd", + Namespace: argocdNS.Name, + }, + Spec: argov1beta1api.ArgoCDSpec{ + SourceNamespaces: []string{sourceNS1.Name}, + Notifications: argov1beta1api.ArgoCDNotifications{ + Enabled: false, + SourceNamespaces: []string{sourceNS1.Name}, + }, + }, + } + Expect(k8sClient.Create(ctx, argocd)).To(Succeed()) + + By("waiting for Argo CD to be available") + Eventually(argocd, "5m", "5s").Should(argocdFixture.BeAvailable()) + + By("verifying resources are NOT created") + notifCfg := &argov1alpha1api.NotificationsConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default-notifications-configuration", + Namespace: sourceNS1.Name, + }, + } + Consistently(notifCfg).Should(k8sFixture.NotExistByName()) + + By("enabling notifications") + argocdFixture.Update(argocd, func(ac *argov1beta1api.ArgoCD) { + ac.Spec.Notifications.Enabled = true + }) + + By("waiting for Argo CD to reconcile") + Eventually(argocd, "2m", "5s").Should(argocdFixture.BeAvailable()) + Eventually(argocd, "4m", "5s").Should(argocdFixture.HaveNotificationControllerStatus("Running")) + + By("verifying resources are now created") + Eventually(notifCfg, "3m", "5s").Should(k8sFixture.ExistByName()) + + roleName := "example-argocd-" + argocdNS.Name + "-notifications" + role := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleName, + Namespace: sourceNS1.Name, + }, + } + Eventually(role, "3m", "5s").Should(k8sFixture.ExistByName()) + + roleBinding := &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleName, + Namespace: sourceNS1.Name, + }, + } + Eventually(roleBinding, "3m", "5s").Should(k8sFixture.ExistByName()) + + By("verifying source namespace has the notifications-managed-by-cluster-argocd label") + Eventually(sourceNS1, "2m", "5s").Should(namespaceFixture.HaveLabel(common.ArgoCDNotificationsManagedByClusterArgoCDLabel, argocdNS.Name)) + + By("verifying ClusterRole is created for notifications controller") + notifClusterRole := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-argocd-" + argocdNS.Name + "-argocd-notifications-controller", + }, + } + Eventually(notifClusterRole, "3m", "5s").Should(k8sFixture.ExistByName()) + + By("verifying ClusterRoleBinding is created for notifications controller") + notifClusterRoleBinding := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-argocd-" + argocdNS.Name + "-argocd-notifications-controller", + }, + } + Eventually(notifClusterRoleBinding, "3m", "5s").Should(k8sFixture.ExistByName()) + + }) + + }) + +}) diff --git a/test/openshift/e2e/ginkgo/sequential/1-102_validate_handle_terminating_namespaces_test.go b/test/openshift/e2e/ginkgo/sequential/1-102_validate_handle_terminating_namespaces_test.go index 9418ebff4..f962fdc24 100644 --- a/test/openshift/e2e/ginkgo/sequential/1-102_validate_handle_terminating_namespaces_test.go +++ b/test/openshift/e2e/ginkgo/sequential/1-102_validate_handle_terminating_namespaces_test.go @@ -147,7 +147,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { } return true - }).Should(BeTrue()) + }, "3m", "5s").Should(BeTrue(), "RoleBindings were not created in John namespace within timeout") By("creating a test Argo CD Application targeting john NS")